You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dlab.apache.org by an...@apache.org on 2019/03/01 11:09:57 UTC
[incubator-dlab] 03/03: [DLAB-375]: moved modal dialogs;
admin preferences
This is an automated email from the ASF dual-hosted git repository.
ankovalyshyn pushed a commit to branch DLAB-375
in repository https://gitbox.apache.org/repos/asf/incubator-dlab.git
commit 6e29cd251509ad495c2d0ce81200dab7f3ede50b
Author: Andriana Kovalyshyn <An...@epam.com>
AuthorDate: Fri Mar 1 13:09:41 2019 +0200
[DLAB-375]: moved modal dialogs; admin preferences
---
.../backup-dilog/backup-dilog.component.html | 81 ++++++
.../backup-dilog/backup-dilog.component.scss | 25 ++
.../backup-dilog/backup-dilog.component.ts | 74 ++++++
.../manage-environment-dilog.component.html | 72 ++++++
.../manage-environment-dilog.component.scss | 86 +++++++
.../manage-environment-dilog.component.ts | 126 +++++++++
.../group-name-validarion.directive.ts | 36 +++
.../manage-roles-groups.component.html | 154 +++++++++++
.../manage-roles-groups.component.scss | 282 +++++++++++++++++++++
.../manage-roles-groups.component.ts | 189 ++++++++++++++
.../ssn-monitor/ssn-monitor.component.html | 116 +++++++++
.../ssn-monitor/ssn-monitor.component.scss | 49 ++++
.../ssn-monitor/ssn-monitor.component.ts | 58 +++++
13 files changed, 1348 insertions(+)
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.html b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.html
new file mode 100644
index 0000000..862b8d0
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.html
@@ -0,0 +1,81 @@
+<!--
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+
+<modal-dialog #bindDialog modalClass="backup-dialog modal-sm">
+ <modal-header>
+ <h4 class="modal-title">Backup options</h4>
+ </modal-header>
+ <modal-content>
+ <div class="content-box" *ngIf="backupOptions">
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.configFiles[0] === 'all'"
+ (change)="onHoldChanged($event, 'configFiles')">
+ <span class="hold-label">Configuration files</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.keys[0] === 'all'"
+ (change)="onHoldChanged($event, 'keys')">
+ <span class="hold-label">User keys</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.databaseBackup"
+ (change)="onHoldChanged($event, 'databaseBackup')">
+ <span class="hold-label">Database</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.certificates[0] === 'all'"
+ (change)="onHoldChanged($event, 'certificates')">
+ <span class="hold-label">SSL Certificates</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.jars[0] === 'all'"
+ (change)="onHoldChanged($event, 'jars')">
+ <span class="hold-label">JAR files</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="hold-block">
+ <mat-slide-toggle
+ labelPosition="before"
+ [checked]="backupOptions.logsBackup"
+ (change)="onHoldChanged($event, 'logsBackup')">
+ <span class="hold-label">Log files</span>
+ </mat-slide-toggle>
+ </div>
+ <div class="text-center m-top-30 m-bott-10">
+ <button mat-raised-button type="button" class="butt" (click)="bindDialog.close(); backupOptions.setDegault()">
+ Cancel
+ </button>
+ <button mat-raised-button type="button" (click)="applyOptions()" class="butt butt-success" [disabled]="!valid">Apply</button>
+ </div>
+ </div>
+ </modal-content>
+</modal-dialog>
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.scss b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.scss
new file mode 100644
index 0000000..6d36f82
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.scss
@@ -0,0 +1,25 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+.hold-block {
+ padding: 5px 10px;
+ transition: all 0.4s ease-out;
+ &:hover {
+ color: #35afd5;
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.ts b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.ts
new file mode 100644
index 0000000..4d0e9e9
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/backup-dilog/backup-dilog.component.ts
@@ -0,0 +1,74 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+import { Component, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
+import { DICTIONARY } from '../../../dictionary/global.dictionary';
+
+import { BackupOptionsModel } from '../management.model';
+
+@Component({
+ selector: 'dlab-backup-dilog',
+ templateUrl: './backup-dilog.component.html',
+ styleUrls: ['./backup-dilog.component.scss']
+})
+export class BackupDilogComponent implements OnInit {
+ readonly DICTIONARY = DICTIONARY;
+ public backupOptions: BackupOptionsModel = new BackupOptionsModel([], [], [], [], false, false);
+ public valid: boolean = true;
+
+ @ViewChild('bindDialog') bindDialog;
+ @Output() backupOpts: EventEmitter<{}> = new EventEmitter();
+
+ ngOnInit() {
+ this.backupOptions.setDegault();
+ this.bindDialog.onClosing = () => this.backupOptions.setDegault();
+ }
+
+ public open(param): void {
+ this.valid = true;
+ this.bindDialog.open(param);
+ }
+
+ public onHoldChanged($event, key): void {
+ this.backupOptions[key] instanceof Array
+ ? (this.backupOptions[key][0] = $event.checked ? 'all' : 'skip')
+ : (this.backupOptions[key] = !this.backupOptions[key]);
+
+ this.checkValidity();
+ }
+
+ public applyOptions(): void {
+ this.backupOpts.emit(this.backupOptions);
+ this.backupOptions.setDegault();
+ this.bindDialog.close();
+ }
+
+ private checkValidity(): void {
+ const items = [];
+
+ Object.keys(this.backupOptions).forEach(el => {
+ if (this.backupOptions[el] instanceof Array) {
+ if (this.backupOptions[el][0] && this.backupOptions[el][0] !== 'skip') items.push(this.backupOptions[el][0]);
+ } else {
+ if (this.backupOptions[el]) items.push(this.backupOptions[el]) ;
+ }
+ });
+
+ this.valid = items.length > 0;
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.html b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.html
new file mode 100644
index 0000000..200dd61
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.html
@@ -0,0 +1,72 @@
+<!--
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+
+<modal-dialog #bindDialog modalClass="manage-env-dialog modal-xl-s">
+ <modal-header>
+ <h4 class="modal-title">Manage environment</h4>
+ </modal-header>
+ <modal-content>
+ <div class="content-box">
+ <div *ngIf="usersList.length">
+ <form [formGroup]="manageUsersForm" (submit)="setBudgetLimits(manageUsersForm.value)" novalidate>
+ <mat-list>
+ <mat-list-item class="list-header">
+ <div class="username">User</div>
+ <div class="quotes" *ngIf="DICTIONARY.cloud_provider !== 'gcp'">Limit</div>
+ <div class="action">Actions</div>
+ </mat-list-item>
+ <div class="scrolling-content" id="scrolling" formArrayName="users">
+ <mat-list-item *ngFor="let item of usersEnvironments.controls; let i=index" [formGroupName]="i" class="list-item">
+ <div class="username ellipsis">{{ manageUsersForm.controls['users'].controls[i].value['name'] }}</div>
+ <div class="quotes" *ngIf="DICTIONARY.cloud_provider !== 'gcp'">
+ <input type="number" min="0" placeholder="Enter limit, in USD" formControlName="budget">
+ <span class="danger_color" *ngIf="!manageUsersForm?.controls['users'].controls[i].controls['budget'].valid && !manageUsersForm?.controls['users'].controls[i].controls['budget'].hasError('overrun')">Only positive integers are allowed</span>
+ <span class="danger_color" *ngIf="manageUsersForm?.controls['users'].controls[i].controls['budget'].hasError('overrun')">Per-user quotes cannot be greater than total budget</span>
+ </div>
+ <div class="action">
+ <span *ngIf=" manageUsersForm.controls['users'].controls[i].value['status'] === 'ACTIVE'; else not_active" matTooltip="Stop" matTooltipPosition="above" (click)="applyAction('stop', item)"><i class="material-icons">pause_circle_outline</i></span>
+
+ <ng-template #not_active>
+ <span class="disabled" matTooltip="User's environment is not active" matTooltipPosition="above">
+ <i class="material-icons">pause_circle_outline</i>
+ </span>
+ </ng-template>
+
+ <span matTooltip="Terminate" matTooltipPosition="above" (click)="applyAction('terminate', item)"><i class="material-icons">phonelink_off</i></span>
+ </div>
+ </mat-list-item>
+ </div>
+ <div class="control-group total-budget" *ngIf="DICTIONARY.cloud_provider !== 'gcp'">
+ <label class="label">Total budget</label>
+ <div class="control">
+ <input type="number" formControlName="total" placeholder="Enter total budget, in USD">
+ <span class="danger_color" *ngIf="manageUsersForm?.controls['total'].hasError('overrun')">Total budget cannot be lower than a sum of users quotes</span>
+ </div>
+ </div>
+ <div class="text-center m-top-30" *ngIf="DICTIONARY.cloud_provider !== 'gcp'">
+ <button mat-raised-button type="button" (click)="bindDialog.close()" class="butt action">Cancel</button>
+ <button mat-raised-button type="submit" [disabled]="!manageUsersForm.valid"
+ class="butt butt-success" [ngClass]="{'not-allowed': !manageUsersForm.valid}">Apply</button>
+ </div>
+ </mat-list>
+ </form>
+ </div>
+ <div class="info message" *ngIf="!usersList.length">No active users environments</div>
+ </div>
+ </modal-content>
+</modal-dialog>
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.scss b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.scss
new file mode 100644
index 0000000..c10c2e8
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.scss
@@ -0,0 +1,86 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+.manage-env-dialog {
+ .mat-list {
+ .mat-list-item {
+ height: 60px;
+ position: relative;
+ .mat-list-item-content {
+ padding: 0 !important;
+ justify-content: space-between;
+ color: #577289;
+ }
+ }
+ }
+ .scrolling-content {
+ max-height: 300px;
+ min-height: 85px;
+ overflow-y: auto;
+ }
+ .username {
+ width: 45%;
+ }
+ .quotes {
+ width: 40%;
+ margin-right: 10px;
+ position: relative;
+ .danger_color {
+ position: absolute;
+ left: 0;
+ top: 36px;
+ }
+ }
+ .action {
+ width: 15%;
+ span {
+ padding: 3px;
+ cursor: pointer;
+ &:hover {
+ color: #35afd5;
+ }
+ i {
+ font-size: 20px;
+ }
+ }
+ .disabled {
+ cursor: not-allowed !important;
+ pointer-events: all;
+ opacity: .6;
+ &:hover {
+ color: #6b8299;
+ }
+ }
+ }
+ .total-budget {
+ border-top: 1px solid #edf1f5;
+ padding-top: 15px;
+ .control {
+ position: relative;
+ width: 56%;
+ .danger_color {
+ position: absolute;
+ left: 0;
+ bottom: -20px;
+ }
+ }
+ .label {
+ width: 44%;
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.ts b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.ts
new file mode 100644
index 0000000..212a3b6
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-environment/manage-environment-dilog.component.ts
@@ -0,0 +1,126 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+import { Component, ViewChild, Output, EventEmitter, ViewEncapsulation, Inject } from '@angular/core';
+import { Validators, FormBuilder, FormGroup, FormArray } from '@angular/forms';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+import { DICTIONARY } from '../../../dictionary/global.dictionary';
+
+@Component({
+ selector: 'dlab-manage-env-dilog',
+ templateUrl: './manage-environment-dilog.component.html',
+ styleUrls: ['./manage-environment-dilog.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class ManageEnvironmentComponent {
+ readonly DICTIONARY = DICTIONARY;
+ public usersList: Array<string> = [];
+ public manageUsersForm: FormGroup;
+ public manageTotalsForm: FormGroup;
+
+ @ViewChild('bindDialog') bindDialog;
+ @Output() manageEnv: EventEmitter<{}> = new EventEmitter();
+ @Output() setBudget: EventEmitter<{}> = new EventEmitter();
+
+ constructor(
+ private _fb: FormBuilder,
+ public dialog: MatDialog
+ ) { }
+
+ get usersEnvironments(): FormArray{
+ return <FormArray>this.manageUsersForm.get('users');
+ }
+
+ public open(param, data, settings): void {
+ this.usersList = data;
+ !this.manageUsersForm && this.initForm();
+
+ this.manageUsersForm.setControl('users',
+ this._fb.array((this.usersList || []).map((x: any) => this._fb.group({
+ name: x.name, budget: [x.budget, [Validators.min(0), this.userValidityCheck.bind(this)]], status: x.status
+ }))));
+ this.manageUsersForm.controls['total'].setValue(settings.conf_max_budget || null);
+ this.bindDialog.open(param);
+ }
+
+ public setBudgetLimits(value) {
+ this.setBudget.emit(value);
+ this.bindDialog.close();
+ }
+
+ public applyAction(action, user) {
+ const dialogRef: MatDialogRef<ConfirmActionDialogComponent> = this.dialog.open(
+ ConfirmActionDialogComponent, { data: {action, user: user.value.name}, width: '550px' });
+ dialogRef.afterClosed().subscribe(result => {
+ if (result) this.manageEnv.emit({action, user: user.value.name});
+ });
+ }
+
+ private initForm(): void {
+ this.manageUsersForm = this._fb.group({
+ total: [null, [Validators.min(0), this.totalValidityCheck.bind(this)]],
+ users: this._fb.array([this._fb.group({ name: '', budget: null, status: ''})])
+ });
+ }
+
+ private getCurrentUsersTotal(): number {
+ return this.manageUsersForm.value.users.reduce((memo, el) => memo += el.budget, 0);
+ }
+
+ private getCurrentTotalValue(): number {
+ return this.manageUsersForm.value.total;
+ }
+
+ private totalValidityCheck(control) {
+ return (control && control.value)
+ ? (control.value >= this.getCurrentUsersTotal() ? null : { overrun: true })
+ : null;
+ }
+
+ private userValidityCheck(control) {
+ if (control && control.value) {
+ return (this.getCurrentTotalValue() && this.getCurrentTotalValue() < this.getCurrentUsersTotal()) ? { overrun: true } : null;
+ }
+ }
+}
+
+@Component({
+ selector: 'dialog-result-example-dialog',
+ template: `
+ <div mat-dialog-content class="content">
+ <p>Environment of <b>{{ data.user }}</b> will be
+ <span *ngIf="data.action === 'terminate'"> terminated.</span>
+ <span *ngIf="data.action === 'stop'">stopped.</span>
+ </p>
+ <p class="m-top-20"><strong>Do you want to proceed?</strong></p>
+ </div>
+ <div class="text-center">
+ <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>
+ <button type="button" class="butt butt-success" mat-raised-button (click)="dialogRef.close(true)">Yes</button>
+ </div>
+ `,
+ styles: [`
+ .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400 }
+ `]
+})
+export class ConfirmActionDialogComponent {
+ constructor(
+ public dialogRef: MatDialogRef<ConfirmActionDialogComponent>,
+ @Inject(MAT_DIALOG_DATA) public data: any
+ ) { }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/group-name-validarion.directive.ts b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/group-name-validarion.directive.ts
new file mode 100644
index 0000000..2b35c8d
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/group-name-validarion.directive.ts
@@ -0,0 +1,36 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+import { Directive, forwardRef, Input } from '@angular/core';
+import { AbstractControl, NG_VALIDATORS, Validator } from '@angular/forms';
+
+@Directive({
+ selector: '[validator][ngModel],[group-dir][ngFormControl]',
+ providers: [{
+ multi: true,
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => GroupNameValidationDirective)
+ }]
+})
+export class GroupNameValidationDirective implements Validator {
+ @Input() validator: Function;
+
+ validate(control: AbstractControl): { [key: string]: any; } {
+ return this.validator(control);
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.html b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.html
new file mode 100644
index 0000000..0803011
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.html
@@ -0,0 +1,154 @@
+<!--
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+
+<modal-dialog #bindDialog modalClass="manage-roles-dialog modal-xxl">
+ <modal-header>
+ <h4 class="modal-title">Manage roles</h4>
+ </modal-header>
+ <modal-content>
+ <div class="content-box">
+ <button mat-raised-button class="butt add-group" (click)="stepperView = !stepperView">
+ <i class="material-icons">people_outline</i>Add group
+ </button>
+ <mat-horizontal-stepper #stepper *ngIf="stepperView" class="stepper ani">
+ <mat-step>
+ <ng-template matStepLabel>Groups</ng-template>
+ <div class="inner-step mat-reset">
+ <input [validator]="groupValidarion()" type="text" placeholder="Enter group name" [(ngModel)]="setupGroup" #setupGroupName="ngModel">
+ <div class="danger_color" *ngIf="setupGroupName.errors?.patterns && setupGroupName.dirty">Group name can only contain letters, numbers, hyphens and '_'</div>
+ <div class="danger_color" *ngIf="setupGroupName.errors?.duplicate && setupGroupName.dirty">Group name already exists</div>
+ </div>
+ <div class="text-center m-bott-10">
+ <button mat-raised-button (click)="resetDialog()" class="butt">Cancel</button>
+ <button mat-raised-button matStepperNext class="butt">Next<i class="material-icons">keyboard_arrow_right</i></button>
+ </div>
+ </mat-step>
+ <mat-step>
+ <ng-template matStepLabel>Roles</ng-template>
+ <div class="inner-step mat-reset">
+ <div class="selector-wrapper">
+ <!-- <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'role'" [items]="rolesList" [model]="setupRoles"></multi-select-dropdown> -->
+ <mat-form-field>
+ <mat-select multiple [compareWith]="compareObjects" name="roles" [(value)]="setupRoles" placeholder="Select roles">
+ <mat-option class="multiple-select" disabled>
+ <a class="select ani" (click)="selectAllOptions(setupRoles, rolesList)">
+ <i class="material-icons">playlist_add_check</i> All
+ </a>
+ <a class="deselect ani" (click)="selectAllOptions(setupRoles)">
+ <i class="material-icons">clear</i> None
+ </a>
+ </mat-option>
+ <mat-option *ngFor="let role of rolesList" [value]="role">
+ {{ role }}
+ </mat-option>
+ </mat-select>
+ <button class="caret">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </div>
+ </div>
+ <div class="text-center m-bott-10">
+ <button mat-raised-button matStepperPrevious class="butt"><i class="material-icons">keyboard_arrow_left</i>Back</button>
+ <button mat-raised-button (click)="resetDialog()" class="butt">Cancel</button>
+ <button mat-raised-button matStepperNext class="butt">Next<i class="material-icons">keyboard_arrow_right</i></button>
+ </div>
+ </mat-step>
+ <mat-step>
+ <ng-template matStepLabel>Users</ng-template>
+ <div class="inner-step mat-reset">
+ <input type="text" placeholder="Enter user login" [(ngModel)]="setupUser">
+ </div>
+ <div class="text-center m-bott-10">
+ <button mat-raised-button matStepperPrevious class="butt"><i class="material-icons">keyboard_arrow_left</i>Back</button>
+ <button mat-raised-button (click)="resetDialog()" class="butt">Cancel</button>
+ <button mat-raised-button (click)="manageAction('create', 'group')" class="butt butt-success"
+ [disabled]="!setupGroup || setupGroupName.errors?.pattern || !setupRoles.length > 0">Create</button>
+ </div>
+ </mat-step>
+ </mat-horizontal-stepper>
+ <mat-divider></mat-divider>
+ <div *ngIf="groupsData.length" class="ani">
+ <table class="dashboard_table">
+ <tr>
+ <th class="th_name groups">Name</th>
+ <th class="roles">Roles</th>
+ <th class="users">Users</th>
+ <th class="th_actions">Action</th>
+ </tr>
+ <tr *ngFor="let item of groupsData" class="dashboard_table_body filter-row">
+ <td>{{ item.group }}</td>
+ <td class="roles mat-reset">
+ <div class="selector-wrapper-edit">
+ <mat-form-field>
+ <mat-select multiple [compareWith]="compareObjects" name="selected_roles" [(value)]="item.selected_roles" placeholder="Select roles">
+ <mat-option class="multiple-select" disabled>
+ <a class="select ani" (click)="selectAllOptions(item, rolesList, 'selected_roles')">
+ <i class="material-icons">playlist_add_check</i> All
+ </a>
+ <a class="deselect ani" (click)="selectAllOptions(item, null, 'selected_roles')">
+ <i class="material-icons">clear</i> None
+ </a>
+ </mat-option>
+ <mat-option *ngFor="let role of rolesList" [value]="role">
+ {{ role }}
+ </mat-option>
+ </mat-select>
+ <button class="caret ani">
+ <i class="material-icons">keyboard_arrow_down</i>
+ </button>
+ </mat-form-field>
+ </div>
+ </td>
+ <td class="users-list ani">
+ <mat-form-field class="chip-list">
+ <input #user matInput placeholder="Enter user login" pattern="[@.-_0-9a-zA-Z]" (keydown.enter)="addUser(user.value, item); user.value = ''">
+ <button mat-icon-button matSuffix (click)="addUser(user.value, item); user.value = ''">
+ <mat-icon>person_add</mat-icon>
+ </button>
+ </mat-form-field>
+ <div class="list-selected list-container ani">
+ <mat-chip-list>
+ <mat-chip *ngFor="let user of item.users">
+ {{ user }}
+ <a class="material-icons" (click)="removeUser(item.users, user)">clear</a>
+ </mat-chip>
+ </mat-chip-list>
+ </div>
+ </td>
+ <td class="actions">
+ <button mat-icon-button class="reset ani" (click)="manageAction('delete', 'group', item)">
+ <i class="material-icons">close</i>
+ </button>
+
+ <button mat-icon-button class="apply ani" matTooltip="Group cannot be updated without any selected role"
+ matTooltipPosition="above"
+ [matTooltipDisabled]="item.selected_roles.length > 0">
+ <span class="mid" [ngClass]="{ 'not-allowed' : !item.selected_roles.length }"
+ (click)="manageAction('update', 'group', item)">
+ <i class="material-icons">done</i>
+ </span>
+ </button>
+ </td>
+ </tr>
+ </table>
+ </div>
+ </div>
+ </modal-content>
+</modal-dialog>
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.scss b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.scss
new file mode 100644
index 0000000..205fd83
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.scss
@@ -0,0 +1,282 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+::ng-deep .mat-option:first-child .mat-pseudo-checkbox { display: none; }
+
+.caret {
+ width: 40px;
+ color: #4ab8dc;
+ border: none;
+ border-left: 1px solid #ececec;
+ background-color: #fff;
+ position: absolute;
+ right: 0;
+ top: 0px;
+ height: 36px;
+ cursor: pointer;
+ &.not-allowed {
+ background-color: #dcdcdc;
+ }
+}
+
+
+.content-box {
+ padding: 20px 30px 35px;
+ height: 85vh;
+ overflow-y: auto;
+}
+.no-details {
+ color: #d8d8d8;
+}
+.mat-divider {
+ margin: 10px 0;
+}
+.stepper {
+ height: 190px;
+ margin-top: 10px;
+ .inner-step {
+ height: 70px;
+ padding: 5px;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ text-align: center;
+ input {
+ width: 490px;
+ align-self: center;
+ }
+ .caret {
+ i {
+ margin-top: 3px;
+ }
+ }
+ }
+}
+.selector-wrapper {
+ display: flex;
+ align-self: center;
+ width: 490px;
+ height: 36px;
+ padding-left: 10px;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 15px;
+ font-weight: 300;
+ box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+ mat-form-field {
+ width: 100%;
+ .mat-form-field-wrapper {
+ padding-bottom: 0;
+ }
+ .mat-icon {
+ font-size: 20px;
+ }
+ }
+ .dropdown-multiselect {
+ width: 100% !important;
+ > button {
+ padding: 6px 22px;
+ }
+ }
+}
+.list-header {
+ padding: 0 15px;
+}
+.scrolling-content {
+ max-height: 450px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ display: block;
+ padding: 15px 5px;
+ &.stepper-opened {
+ height: 250px;
+ }
+}
+.roles {
+ width: 30%;
+ padding: 0 10px;
+ .selector-wrapper-edit {
+ position: relative;
+ display: flex;
+ justify-content: space-between;
+ height: 36px;
+ padding-left: 10px;
+ background: #fff;
+ box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+ multi-select-dropdown {
+ width: 100%;
+ .dropdown-multiselect {
+ > button {
+ padding: 8px 22px 5px;
+ }
+ }
+ }
+ .caret {
+ &:hover {
+ box-shadow: 0 3px 1px -10px rgba(0, 0, 0, 0.2), 0 2px 1px 0 rgba(0, 0, 0, 0.17), 0 1px 5px 0 rgba(0, 0, 0, 0.12)
+ }
+ }
+ }
+}
+.groups {
+ width: 20%;
+}
+.users {
+ width: 30%;
+ padding: 0 10px;
+}
+.users-list {
+ padding: 5px 10px;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 15px;
+ font-weight: 300;
+ color: #577289;
+ position: relative;
+ i {
+ color: #FF5722;
+ font-size: 18px;
+ cursor: pointer;
+ }
+ .list-selected {
+ width: 100%;
+ margin-top: 50px;
+ height: inherit;
+ }
+}
+.expanded-panel {
+ display: flex;
+ align-items: flex-end;
+ .add-input-block {
+ display: flex;
+ height: 36px;
+ padding-right: 0;
+ outline: none;
+ box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
+ input {
+ height: 36px;
+ padding: 0;
+ padding-left: 10px;
+ width: 170px;
+ }
+ .caret {
+ width: 50px;
+ }
+ }
+}
+
+
+mat-expansion-panel-header {
+ &.mat-expansion-panel-header {
+ font-family: 'Open Sans', sans-serif;
+ font-size: 15px;
+ font-weight: 300;
+ color: #577289;
+ }
+}
+
+.mat-step-header {
+ .mat-step-icon {
+ background-color: #36afd5 !important;
+ }
+ .mat-step-label {
+ font-family: 'Open Sans', sans-serif;
+ font-size: 16px;
+ font-weight: 300;
+ }
+}
+
+.dashboard_table {
+ .dashboard_table_body {
+ .actions {
+ padding: 10px 0;
+ }
+ &.filter-row {
+ .actions {
+ button {
+ background: none;
+ .mid {
+ vertical-align: sub;
+ }
+ }
+ }
+ }
+ }
+ .th_actions {
+ width: 10%;
+ }
+}
+
+.mat-chip:not(.mat-basic-chip) {
+ transition: box-shadow 280ms cubic-bezier(.4,0,.2,1);
+ padding: 7px 0 7px 10px;
+ border-radius: 24px;
+ cursor: default;
+ display: inline-block;
+ position: relative;
+ padding-right: 25px;
+ white-space: nowrap;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin: 2px !important;
+}
+mat-chip.mat-chip a {
+ position: absolute;
+ right: 15px;
+}
+mat-form-field.chip-list {
+ &.mat-form-field {
+ position: absolute;
+ top: 2px;
+ width: 94%;
+ font-size: 16px;
+ font-weight: 300;
+ }
+}
+
+.mat-raised-button {
+ &.butt {
+ &.butt-success {
+ margin-left: 0;
+ }
+ }
+}
+.multiple-select {
+ border-bottom: 1px solid #dedbdb;
+ padding: 0;
+ a {
+ display: inline-block;
+ width: 50%;
+ padding: 0 15px;
+ vertical-align: middle;
+ color: #575757;
+ cursor: pointer;
+ i {
+ vertical-align: sub;
+ font-size: 20px;
+ }
+ &:hover {
+ color: #4eaf3e;
+ background: #f9fafb;
+ }
+ &.deselect {
+ &:hover {
+ color: #f1696e;
+ }
+ }
+ }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.ts b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.ts
new file mode 100644
index 0000000..25124ca
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/manage-roles-groups/manage-roles-groups.component.ts
@@ -0,0 +1,189 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+import { Component, OnInit, ViewChild, Output, EventEmitter, Inject } from '@angular/core';
+import { ValidatorFn, FormControl, NgModel } from '@angular/forms';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
+import { DICTIONARY } from '../../../dictionary/global.dictionary';
+
+@Component({
+ selector: 'dlab-manage-roles-groups',
+ templateUrl: './manage-roles-groups.component.html',
+ styleUrls: ['../../resources/resources-grid/resources-grid.component.css', './manage-roles-groups.component.scss']
+})
+export class ManageRolesGroupsComponent implements OnInit {
+ readonly DICTIONARY = DICTIONARY;
+
+ public groupsData: Array<any> = [];
+ public roles: Array<any> = [];
+ public rolesList: Array<string> = [];
+ public setupGroup: string = '';
+ public setupUser: string = '';
+ public manageUser: string = '';
+ public setupRoles: Array<string> = [];
+ public updatedRoles: Array<string> = [];
+ public delimitersRegex = /[-_]?/g;
+ public groupnamePattern = new RegExp(/^[a-zA-Z0-9_\-]+$/);
+
+ @ViewChild('bindDialog') bindDialog;
+ @Output() manageRolesGroupAction: EventEmitter<{}> = new EventEmitter();
+ stepperView: boolean = false;
+
+ constructor(public dialog: MatDialog) { }
+
+ ngOnInit() {
+ this.bindDialog.onClosing = () => this.resetDialog();
+ }
+
+ public open(param, groups, roles): void {
+ this.roles = roles;
+ this.rolesList = roles.map(role => role.description);
+ this.updateGroupData(groups);
+
+ this.stepperView = false;
+ this.bindDialog.open(param);
+ }
+
+ public onUpdate($event) {
+ if ($event.type === 'role') {
+ this.setupRoles = $event.model;
+ } else {
+ this.updatedRoles = $event.model;
+ }
+ $event.$event.preventDefault();
+ }
+
+ public selectAllOptions(item, values, byKey?) {
+ byKey ? (item[byKey] = values ? values : []) : this.setupRoles = values ? values : [];
+ }
+
+ public manageAction(action: string, type: string, item?: any, value?) {
+ if (action === 'create') {
+ this.manageRolesGroupAction.emit(
+ { action, type, value: {
+ name: this.setupGroup,
+ users: this.setupUser ? this.setupUser.split(',').map(elem => elem.trim()) : [],
+ roleIds: this.extractIds(this.roles, this.setupRoles)
+ }
+ });
+ this.stepperView = false;
+ } else if (action === 'delete') {
+ const data = (type === 'users') ? {group: item.group, user: value} : {group: item.group, id: item};
+ const dialogRef: MatDialogRef<ConfirmDeleteUserAccountDialogComponent> = this.dialog.open(
+ ConfirmDeleteUserAccountDialogComponent,
+ { data: data, width: '550px' }
+ );
+
+ dialogRef.afterClosed().subscribe(result => {
+ if (result) {
+ const emitValue = (type === 'users')
+ ? {action, type, id: item.name, value: { user: value, group: item.group }}
+ : {action, type, id: item.name, value: item.group} ;
+ this.manageRolesGroupAction.emit(emitValue);
+ }
+ });
+ } else if (action === 'update') {
+ this.manageRolesGroupAction.emit({action, type, value: {
+ name: item.group,
+ roleIds: this.extractIds(this.roles, item.selected_roles),
+ users: item.users || [] }});
+ }
+ this.resetDialog();
+ }
+
+ public extractIds(sourceList, target) {
+ return sourceList.reduce((acc, item) => {
+ target.includes(item.description) && acc.push(item._id);
+ return acc;
+ }, []);
+ }
+
+ public updateGroupData(groups) {
+ this.groupsData = groups;
+
+ this.groupsData.forEach(item => {
+ item.selected_roles = item.roles.map(role => role.description);
+ });
+ }
+
+ public groupValidarion(): ValidatorFn {
+
+ const duplicateList: any = this.groupsData.map(item => item.group);
+ return <ValidatorFn>((control: FormControl) => {
+ if (control.value && duplicateList.includes(this.delimitersFiltering(control.value)))
+ return { duplicate: true };
+
+ if (control.value && !this.groupnamePattern.test(control.value))
+ return { patterns: true };
+
+ return null;
+ });
+ }
+
+ public compareObjects(o1: any, o2: any): boolean {
+ return o1.toLowerCase() === o2.toLowerCase();
+ }
+
+ public delimitersFiltering(resource): string {
+ return resource.replace(this.delimitersRegex, '').toString().toLowerCase();
+ }
+
+ public resetDialog() {
+ this.stepperView = false;
+ this.setupGroup = '';
+ this.setupUser = '';
+ this.manageUser = '';
+ this.setupRoles = [];
+ this.updatedRoles = [];
+ }
+
+ public removeUser(list, item): void {
+ list.splice(list.indexOf(item), 1);
+ }
+
+ public addUser(value: string, item): void {
+ if (value && value.trim()) {
+ item.users instanceof Array ? item.users.push(value.trim()) : item.users = [value.trim()];
+ }
+ }
+}
+
+
+@Component({
+ selector: 'dialog-result-example-dialog',
+ template: `
+ <div mat-dialog-content class="content">
+ <p *ngIf="data.user">User <strong>{{ data.user }}</strong> will be deleted from <strong>{{ data.group }}</strong> group.</p>
+ <p *ngIf="data.id">Group <strong>{{ data.group }}</strong> will be decommissioned.</p>
+ <p class="m-top-20"><strong>Do you want to proceed?</strong></p>
+ </div>
+ <div class="text-center">
+ <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">No</button>
+ <button type="button" class="butt butt-success" mat-raised-button (click)="dialogRef.close(true)">Yes</button>
+ </div>
+ `,
+ styles: [`
+ .content { color: #718ba6; padding: 20px 50px; font-size: 14px; font-weight: 400 }
+ `]
+})
+export class ConfirmDeleteUserAccountDialogComponent {
+ constructor(
+ public dialogRef: MatDialogRef<ConfirmDeleteUserAccountDialogComponent>,
+ @Inject(MAT_DIALOG_DATA) public data: any
+ ) { }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.html b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.html
new file mode 100644
index 0000000..94f7564
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.html
@@ -0,0 +1,116 @@
+<!--
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+
+<modal-dialog #bindDialog modalClass="ssn-monitor-dialog modal-lg">
+ <modal-header>
+ <h4 class="modal-title">SSN Monitor</h4>
+ </modal-header>
+ <modal-content>
+ <div class="content-box" *ngIf="monitorData">
+ <div class="ssn-info">
+ <mat-tab-group *ngIf="monitorData?.processorInfo" [dynamicHeight]="true">
+ <mat-tab label="CPU">
+ <div class="scrolling-content" id="scrolling">
+ <mat-list-item class="list-header">
+ <div class="col">Name</div>
+ <div class="col">{{ monitorData.processorInfo.name }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Vendor</div>
+ <div class="col">{{ monitorData.processorInfo.vendor }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Logical Core Count</div>
+ <div class="col">{{ monitorData.processorInfo.logicalCoreCount }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Physical Core Count</div>
+ <div class="col">{{ monitorData.processorInfo.physicalCoreCount }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Current System Load</div>
+ <div class="col">{{ monitorData.processorInfo.currentSystemLoad /100 | percent:'1.0-2' }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">System Load Average</div>
+ <div class="col">{{ monitorData.processorInfo.systemLoadAverage /100 | percent:'1.0-2' }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">CPU 64 Bit</div>
+ <div class="col">{{ monitorData.processorInfo.cpu64Bit }}</div>
+ </mat-list-item>
+ </div>
+ </mat-tab>
+
+ <mat-tab label="Memory">
+ <div class="scrolling-content" id="scrolling">
+ <mat-list-item class="list-header">
+ <div class="col">Available Memory</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.availableMemory) }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Total Memory</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.totalMemory) }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Swap Total</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.swapTotal) }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Swap Used</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.swapUsed) }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Pages Page In</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.pagesPageIn) }}</div>
+ </mat-list-item>
+ <mat-list-item class="list-header">
+ <div class="col">Pages Page Out</div>
+ <div class="col">{{ convertSize(monitorData.memoryInfo.pagesPageOut) }}</div>
+ </mat-list-item>
+ </div>
+
+ </mat-tab>
+ <mat-tab label="HDD">
+ <div class="scrolling-content" id="scrolling">
+ <div *ngFor="let disk of monitorData.disksInfo; let i = index">
+ <mat-list-item>
+ <div class="col"><strong>Disk {{i +1}}</strong></div>
+ </mat-list-item>
+ <mat-list-item>
+ <div class="col">Used Space</div>
+ <div class="col">{{ convertSize(disk.usedByteSpace) }}</div>
+ </mat-list-item>
+ <mat-list-item>
+ <div class="col">Total Space</div>
+ <div class="col">{{ convertSize(disk.totalByteSpace) }}</div>
+ </mat-list-item>
+ </div>
+ </div>
+ </mat-tab>
+ </mat-tab-group>
+ <div class="text-center">
+ <button type="button" class="butt" mat-raised-button (click)="close()">Close</button>
+ </div>
+ </div>
+ <div class="info message" *ngIf="isEmpty(monitorData)">
+ No ssn monitor data available
+ </div>
+ </div>
+ </modal-content>
+</modal-dialog>
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.scss b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.scss
new file mode 100644
index 0000000..a4d6f9d
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.scss
@@ -0,0 +1,49 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+.ssn-monitor-dialog {
+ .content-box {
+ padding-top: 10px !important;
+ }
+ .ssn-info {
+ min-height: 400px;
+ max-height: 500px;
+ .scrolling-content {
+ max-height: 310px;
+ overflow-y: auto;
+ }
+ .text-center {
+ position: absolute;
+ bottom: 20px;
+ left: 0;
+ right: 0;
+ }
+ }
+ .mat-list-item-content {
+ display: flex;
+ justify-content: initial;
+ color: #577289;
+ padding: 15px 5px;
+ border-bottom: 1px solid #f3f2f2;
+ font-size: 15px;
+ .col {
+ width: 50%;
+ font-weight: 300;
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.ts b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.ts
new file mode 100644
index 0000000..b7d5e0c
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/management/ssn-monitor/ssn-monitor.component.ts
@@ -0,0 +1,58 @@
+/***************************************************************************
+
+Copyright (c) 2016, EPAM SYSTEMS INC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+****************************************************************************/
+
+import { Component, OnInit, ViewChild, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
+import { DICTIONARY } from './../../../dictionary/global.dictionary';
+
+@Component({
+ selector: 'dlab-ssn-monitor',
+ templateUrl: './ssn-monitor.component.html',
+ styleUrls: ['./ssn-monitor.component.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class SsnMonitorComponent implements OnInit {
+ readonly DICTIONARY = DICTIONARY;
+
+ public errorMessage: string = '';
+ public monitorData = {};
+
+ @ViewChild('bindDialog') bindDialog;
+ @Output() manageEnv: EventEmitter<{}> = new EventEmitter();
+
+ ngOnInit() {}
+
+ public open(param, data): void {
+ this.monitorData = data || {};
+ this.bindDialog.open(param);
+ }
+ public close(param, data): void {
+ this.bindDialog.close();
+ }
+
+ public isEmpty(obj) {
+ if (obj) return Object.keys(obj).length === 0;
+ }
+
+ public convertSize(bytes) {
+ if (Number(bytes) === 0) return '0 Byte';
+
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
+ return parseFloat((bytes / Math.pow(1024, i)).toFixed(3)) + ' ' + sizes[i];
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@dlab.apache.org
For additional commands, e-mail: commits-help@dlab.apache.org