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>&nbsp;All
+                      </a>
+                      <a class="deselect ani" (click)="selectAllOptions(setupRoles)">
+                        <i class="material-icons">clear</i>&nbsp;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>&nbsp;All
+                      </a>
+                      <a class="deselect ani" (click)="selectAllOptions(item, null, 'selected_roles')">
+                        <i class="material-icons">clear</i>&nbsp;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