You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by hs...@apache.org on 2022/10/05 16:16:21 UTC

[incubator-datalab] 01/01: implemented logic by add new platform, disconnect platform

This is an automated email from the ASF dual-hosted git repository.

hshpak pushed a commit to branch feat/DATALAB-3064/add-new-platform
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 8abd7552942815f14b22862d7e9f70663bf77c91
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Wed Sep 28 15:19:20 2022 +0300

    implemented logic by add new platform, disconnect platform
---
 .../administration/management/management.model.ts  |   6 +
 .../resources/webapp/src/app/core/core.module.ts   |   4 +-
 .../services/applicationServiceFacade.service.ts   |  26 +++-
 .../services/connected-platform-api.service.ts     |  54 +++++++++
 .../app/core/services/image-page-resolve.guard.ts  |   4 +-
 ...ages-page.service.ts => images-page.service.ts} |   2 +-
 .../webapp/src/app/core/services/index.ts          |   2 +-
 .../audit/audit-grid/audit-grid.component.html     | 134 ++++++++++-----------
 .../audit/audit-grid/audit-grid.component.scss     |   2 +-
 ...utational-resource-create-dialog.component.html |   7 +-
 ...mputational-resource-create-dialog.component.ts |   6 +-
 .../connected-platform-dialog.component.html       |  61 ++++++++++
 .../connected-platform-dialog.component.scss       |  93 ++++++++++++++
 .../connected-platform-dialog.component.ts         |  63 ++++++++++
 .../connected-platforms.component.html             |  46 +++++--
 .../connected-platforms.component.scss             |  60 +++++++++
 .../connected-platforms.component.ts               |  99 ++++++++++-----
 ...ms.comnfig.ts => connected-platforms.config.ts} |  11 +-
 ...omponent.scss => connected-platforms.models.ts} |  22 ++--
 .../connected-platforms.module.ts                  |  17 ++-
 .../connected-platforms.service.ts                 |  59 +++++++++
 .../warning-dialog/warning-dialog.component.html   |  28 +++++
 .../warning-dialog.component.scss}                 |  20 +--
 .../warning-dialog/warning-dialog.component.ts     |  49 ++++++++
 .../src/app/resources/images/images.component.html |   3 +-
 .../src/app/resources/images/images.component.ts   |   8 +-
 .../src/app/resources/images/images.config.ts      |   3 +-
 .../src/app/resources/images/images.service.ts     |   4 +-
 .../webapp/src/app/resources/resources.module.ts   |   7 +-
 .../modal-parts/modal-btn/modal-btn.component.html |  34 ++++++
 .../modal-btn/modal-btn.component.scss}            |  22 ++--
 .../modal-parts/modal-btn/modal-btn.component.ts}  |  27 +++--
 .../modal-header/modal-header.component.html       |  27 +++++
 .../modal-header/modal-header.component.scss}      |  31 +++--
 .../modal-header/modal-header.component.ts}        |  25 ++--
 .../modal-parts/modal-parts.module.ts}             |  20 +--
 .../src/app/shared/navbar/navbar.component.html    |   1 +
 .../resources/webapp/src/assets/styles/_theme.scss |   6 +-
 38 files changed, 875 insertions(+), 218 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
index b94e61b7d..8c1103a64 100644
--- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts
@@ -85,8 +85,14 @@ export interface GeneralEnvironmentStatus {
   status: string;
   projectAssigned: boolean;
   bucketBrowser: object;
+  connectedPlatforms: ConnectedPlatformsStatus;
 }
 
+export interface ConnectedPlatformsStatus {
+  add: boolean;
+  disconnect: boolean;
+  view: boolean;
+}
 
 export class ManagementConfigModel {
 
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
index b1fb27d86..5ea5b4fa3 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/core.module.ts
@@ -49,7 +49,7 @@ import { ErrorInterceptor } from './interceptors/error.interceptor';
 
 import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import {  ConfigurationService } from './services/configutration.service';
-import {AuditGuard, OdahuDeploymentService, UserImagesPageService} from './services';
+import {AuditGuard, OdahuDeploymentService, ImagesPageService} from './services';
 import {  ProjectAdminGuard } from './services/projectAdmin.guard';
 
 @NgModule({
@@ -90,7 +90,7 @@ export class CoreModule {
         UserAccessKeyService,
         ConfigurationService,
         OdahuDeploymentService,
-        UserImagesPageService,
+        ImagesPageService,
 
         { provide: MatDialogRef, useValue: {} },
         { provide: MAT_DIALOG_DATA, useValue: [] },
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
index 69cd6e607..032565e54 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/applicationServiceFacade.service.ts
@@ -24,7 +24,8 @@ import { HttpClient } from '@angular/common/http';
 import { Dictionary } from '../collections';
 import { environment } from '../../../environments/environment';
 import { HTTPMethod } from '../util';
-import { ImageActionType, ImageFilterFormValue, ImageParams } from '../../resources/images';
+import { ImageFilterFormValue, ImageParams } from '../../resources/images';
+import { AddPlatformFromValue } from '../../resources/connected-platforms/connected-platforms.models';
 
 // we can now access environment.apiUrl
 const API_URL = environment.apiUrl;
@@ -84,6 +85,7 @@ export class ApplicationServiceFacade {
   private static readonly CONFIG = 'config';
   private static readonly QUOTA = 'quota';
   private static readonly IMAGE_PAGE = 'image_page';
+  private static readonly CONNECTED_PLATFORMS = 'connected_platforms';
 
   private requestRegistry: Dictionary<string>;
 
@@ -201,6 +203,26 @@ export class ApplicationServiceFacade {
       params
       );
   }
+  buildGetConnectedPlatformsPage(): Observable<any> {
+    return this.buildRequest(HTTPMethod.GET,
+      `${this.requestRegistry.Item(ApplicationServiceFacade.CONNECTED_PLATFORMS)}/user`,
+      null
+      );
+  }
+
+  buildAddPlatform(platformParams: AddPlatformFromValue): Observable<any> {
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.CONNECTED_PLATFORMS),
+      platformParams
+    );
+  }
+
+  buildDisconnectPlatform(platformName: string): Observable<any> {
+    return this.buildRequest(HTTPMethod.DELETE,
+      `${this.requestRegistry.Item(ApplicationServiceFacade.CONNECTED_PLATFORMS)}/${platformName}`,
+      null
+    );
+  }
 
   public buildGetTemplatesRequest(params): Observable<any> {
     return this.buildRequest(HTTPMethod.GET,
@@ -750,6 +772,8 @@ export class ApplicationServiceFacade {
       '/api/infrastructure/info');
     this.requestRegistry.Add(ApplicationServiceFacade.IMAGE_PAGE,
       '/api/infrastructure_provision/exploratory_environment/image/user');
+    this.requestRegistry.Add(ApplicationServiceFacade.CONNECTED_PLATFORMS,
+      '/api/connected_platforms');
     this.requestRegistry.Add(ApplicationServiceFacade.EXPLORATORY_ENVIRONMENT,
       '/api/infrastructure_provision/exploratory_environment');
     this.requestRegistry.Add(ApplicationServiceFacade.TEMPLATES,
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/connected-platform-api.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/connected-platform-api.service.ts
new file mode 100644
index 000000000..bd7153080
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/connected-platform-api.service.ts
@@ -0,0 +1,54 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 { Injectable } from '@angular/core';
+import { ApplicationServiceFacade } from './applicationServiceFacade.service';
+import { catchError } from 'rxjs/operators';
+import { ErrorUtils } from '../util';
+import { Observable } from 'rxjs';
+import { AddPlatformFromValue, ConnectedPlatformsInfo } from '../../resources/connected-platforms/connected-platforms.models';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ConnectedPlatformApiService {
+
+  constructor(
+    private applicationServiceFacade: ApplicationServiceFacade
+  ) { }
+
+  getConnectedPlatformsPage(): Observable<ConnectedPlatformsInfo> {
+    return this.applicationServiceFacade.buildGetConnectedPlatformsPage()
+      .pipe(
+        catchError(ErrorUtils.handleServiceError)
+      );
+  }
+
+  addPlatform(platformParams: AddPlatformFromValue) {
+    return  this.applicationServiceFacade.buildAddPlatform(platformParams).pipe(
+      catchError(ErrorUtils.handleServiceError)
+    );
+  }
+
+  disconnectPlatform(platformName: string) {
+    return  this.applicationServiceFacade.buildDisconnectPlatform(platformName).pipe(
+      catchError(ErrorUtils.handleServiceError)
+    );
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/image-page-resolve.guard.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/image-page-resolve.guard.ts
index bf643e851..98483f506 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/image-page-resolve.guard.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/image-page-resolve.guard.ts
@@ -4,7 +4,7 @@ import { Observable, of  } from 'rxjs';
 
 import { ProjectImagesInfo } from '../../resources/images';
 import { switchMap, take } from 'rxjs/operators';
-import { UserImagesPageService } from './user-images-page.service';
+import { ImagesPageService } from './images-page.service';
 
 @Injectable({
   providedIn: 'root'
@@ -12,7 +12,7 @@ import { UserImagesPageService } from './user-images-page.service';
 export class ImagePageResolveGuard implements Resolve<ProjectImagesInfo> {
   constructor(
     private router: Router,
-    private userImagesPageService: UserImagesPageService
+    private userImagesPageService: ImagesPageService
   ) {}
 
   resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ProjectImagesInfo> {
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/images-page.service.ts
similarity index 98%
rename from services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
rename to services/self-service/src/main/resources/webapp/src/app/core/services/images-page.service.ts
index fe41b1483..b87d2bf0c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/user-images-page.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/images-page.service.ts
@@ -33,7 +33,7 @@ import {
 import { ShareDialogData, UserData } from '../../resources/exploratory/image-action-dialog/image-action.model';
 
 @Injectable()
-export class UserImagesPageService {
+export class ImagesPageService {
   constructor(
     private applicationServiceFacade: ApplicationServiceFacade
   ) { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
index 712ffb4ea..b4c34fab2 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/services/index.ts
@@ -41,4 +41,4 @@ export * from './project.service';
 export * from './odahu-deployment.service';
 export * from './endpoint.service';
 export * from './image-page-resolve.guard';
-export * from './user-images-page.service';
+export * from './images-page.service';
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html
index 1e3b8dfbc..69aa1e2f7 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.html
@@ -23,10 +23,10 @@
     <ng-container matColumnDef="date">
       <th mat-header-cell *matHeaderCellDef class="th_date label-header">
         <div class="label"><span class="text">Date</span></div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -42,10 +42,10 @@
     <ng-container matColumnDef="user">
       <th mat-header-cell *matHeaderCellDef class="th_user label-header" [ngStyle]="{'z-index': 99}">
         <div class="label"><span class="audit-user"> User</span></div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -67,10 +67,10 @@
         <div class="label">
           <span class="text"> Action </span>
         </div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -100,10 +100,10 @@
     <ng-container matColumnDef="project">
       <th mat-header-cell *matHeaderCellDef class="th_project label-header">
         <div class="label"><span class="text">Project</span></div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -123,10 +123,10 @@
     <ng-container matColumnDef="resource-type">
       <th mat-header-cell *matHeaderCellDef class="th_resource-type label-header">
         <div class="label"><span class="text">Resource type</span></div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -146,15 +146,15 @@
         <div class="pagination-wrapper">
           <div class="selected-items-wrapper">
             <span>Items per page:</span>
-            <div class="select-wrapper">
+            <div class="select__wrapper">
               <div class="mat-reset">
                 <div class="control selector-wrapper">
                   <mat-form-field>
                     <mat-label></mat-label>
                     <mat-select [(value)]="showItemsPrPage">
-                      <mat-option 
-                        *ngFor="let item of itemsPrPage" 
-                        [value]="item" 
+                      <mat-option
+                        *ngFor="let item of itemsPrPage"
+                        [value]="item"
                         (click)="setItemsPrPage(item)"
                       >
                         {{ item }}
@@ -173,36 +173,36 @@
           </span>
           <span>
             <span [ngClass]="{'not-active':  !isNavigationDisabled || firstItem === 1}">
-              <span 
-                class="navigation-butts" 
-                (click)="loadItemsForPage('first')" 
+              <span
+                class="navigation-butts"
+                (click)="loadItemsForPage('first')"
                 [ngClass]="{'not-allowed': firstItem === 1 || !isNavigationDisabled}"
               >
                 <i class="material-icons">first_page</i>
               </span>
             </span>
             <span [ngClass]="{'not-active': firstItem === 1 || !isNavigationDisabled}">
-              <span 
-                class="navigation-butts" 
-                (click)="loadItemsForPage('previous')" 
+              <span
+                class="navigation-butts"
+                (click)="loadItemsForPage('previous')"
                 [ngClass]="{'not-allowed': firstItem === 1 || !isNavigationDisabled}"
               >
                 <i class="material-icons">keyboard_arrow_left</i>
               </span>
             </span>
             <span [ngClass]="{'not-active': lastItem >= allItems || !isNavigationDisabled}">
-              <span 
-                class="navigation-butts" 
-                (click)="loadItemsForPage('next')" 
+              <span
+                class="navigation-butts"
+                (click)="loadItemsForPage('next')"
                 [ngClass]="{'not-allowed': lastItem >= allItems || !isNavigationDisabled}"
               >
                 <i class="material-icons">keyboard_arrow_right</i>
               </span>
             </span>
             <span [ngClass]="{'not-active': lastItem >= allItems || !isNavigationDisabled}">
-              <span 
-                class="navigation-butts" 
-                (click)="loadItemsForPage('last')" 
+              <span
+                class="navigation-butts"
+                (click)="loadItemsForPage('last')"
                 [ngClass]="{'not-allowed': lastItem >= allItems || !isNavigationDisabled}"
               >
                 <i class="material-icons">last_page</i>
@@ -216,10 +216,10 @@
     <ng-container matColumnDef="resource">
       <th mat-header-cell *matHeaderCellDef class="th_resource label-header">
         <div class="label"><span class="text">Resource</span></div>
-        <button 
-          mat-icon-button 
-          aria-label="More" 
-          class="ar" 
+        <button
+          mat-icon-button
+          aria-label="More"
+          class="ar"
           (click)="toggleFilterRow()"
         >
           <i class="material-icons">
@@ -230,7 +230,7 @@
       </th>
       <td mat-cell *matCellDef="let element" class="th_resource">
         <div class="table-item-wrapper">
-          <span 
+          <span
             class="ellipsis"
             [matTooltip]="element.resourceName || 'N/A'"
             matTooltipPosition="above">
@@ -251,11 +251,11 @@
     <!--   AUDIT FILTER-->
     <ng-container matColumnDef="user-filter">
       <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-        <multi-select-dropdown 
-          *ngIf="filterConfiguration" 
-          (selectionChange)="onUpdate($event)" 
+        <multi-select-dropdown
+          *ngIf="filterConfiguration"
+          (selectionChange)="onUpdate($event)"
           [type]="'users'"
-          [items]="filterConfiguration.users" 
+          [items]="filterConfiguration.users"
           [model]="filterAuditData.users"
         ></multi-select-dropdown>
       </th>
@@ -263,11 +263,11 @@
 
     <ng-container matColumnDef="project-filter">
       <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-        <multi-select-dropdown 
-          *ngIf="filterConfiguration" 
-          (selectionChange)="onUpdate($event)" 
+        <multi-select-dropdown
+          *ngIf="filterConfiguration"
+          (selectionChange)="onUpdate($event)"
           [type]="'projects'"
-          [items]="filterConfiguration.projects" 
+          [items]="filterConfiguration.projects"
           [model]="filterAuditData.projects"
         ></multi-select-dropdown>
       </th>
@@ -277,10 +277,10 @@
       <th mat-header-cell *matHeaderCellDef class="filter-row-item">
         <multi-select-dropdown
           class="audit-resources"
-          *ngIf="filterConfiguration" 
-          (selectionChange)="onUpdate($event)" 
+          *ngIf="filterConfiguration"
+          (selectionChange)="onUpdate($event)"
           [type]="'resources'"
-          [items]="filterConfiguration.resources" 
+          [items]="filterConfiguration.resources"
           [model]="filterAuditData.resources"
         ></multi-select-dropdown>
       </th>
@@ -288,11 +288,11 @@
 
     <ng-container matColumnDef="resource-type-filter">
       <th mat-header-cell *matHeaderCellDef class="filter-row-item">
-        <multi-select-dropdown 
-          *ngIf="filterConfiguration" 
-          (selectionChange)="onUpdate($event)" 
+        <multi-select-dropdown
+          *ngIf="filterConfiguration"
+          (selectionChange)="onUpdate($event)"
           [type]="'resource_types'"
-          [items]="filterConfiguration.resource_types" 
+          [items]="filterConfiguration.resource_types"
           [model]="filterAuditData.resource_types"
         ></multi-select-dropdown>
       </th>
@@ -309,19 +309,19 @@
     <ng-container matColumnDef="filter-buttons" stickyEnd>
       <th mat-header-cell *matHeaderCellDef class="filter-row-item">
         <div class="actions audit-actions">
-          <button 
-            mat-icon-button 
-            class="btn reset" 
-            (click)="resetFilterConfigurations()" 
+          <button
+            mat-icon-button
+            class="btn reset"
+            (click)="resetFilterConfigurations()"
             [disabled]="!isFilterSelected"
           >
             <i class="material-icons">close</i>
           </button>
 
-          <button 
-            mat-icon-button 
-            class="btn apply" 
-            (click)="buildAuditGrid(true)" 
+          <button
+            mat-icon-button
+            class="btn apply"
+            (click)="buildAuditGrid(true)"
             [disabled]="isNavigationDisabled"
           >
             <i class="material-icons">done</i>
@@ -341,9 +341,9 @@
 
     <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr>
 
-    <tr 
-      [hidden]="!collapseFilterRow" 
-      mat-header-row 
+    <tr
+      [hidden]="!collapseFilterRow"
+      mat-header-row
       *matHeaderRowDef="displayedFilterColumns; sticky: true"
       class="filter-row"
     ></tr>
diff --git a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss
index bbee3d383..7775d3b1c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/reports/audit/audit-grid/audit-grid.component.scss
@@ -288,7 +288,7 @@
   display: flex;
   align-items: center;
 
-  .select-wrapper {
+  .select__wrapper {
     margin-left: 20px;
     width: 80px;
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
index ced2fa9bb..dc516ad70 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.html
@@ -29,7 +29,7 @@
           <div class="col">
             <div
               class="control-group"
-              *ngIf="(clusterTypes | isElementAvailable : isHasHDInside) || this.PROVIDER !== providerList.azure"
+              *ngIf="(clusterTypes | isElementAvailable : hasHDInside) || this.PROVIDER !== providerList.azure"
               [hidden]="clusterTypes.length === 1"
             >
               <label class="label">Select cluster type</label>
@@ -133,7 +133,7 @@
             </div>
 
             <div class="control-group"
-                 *ngIf="(clusterTypes | isElementAvailable : isHasHDInside) || PROVIDER !== providerList.azure"
+                 *ngIf="(clusterTypes | isElementAvailable : hasHDInside) || PROVIDER !== providerList.azure"
                  [hidden]="!selectedImage.templates.length"
             >
               <label class="label">Select template</label>
@@ -370,7 +370,8 @@
         </div>
         <div
           class="checkbox-group control-group m-top-10"
-          [hidden]="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'"
+          [hidden]="PROVIDER === 'gcp' && selectedImage?.image === 'docker.datalab-dataengine-service'
+           || templateNameControl.value === templateName.hdInsight"
           *ngIf="notebook_instance?.image !== 'docker.datalab-zeppelin'"
         >
           <div class="d-flex cursor-pointer label" (click)="addAdditionalParams('configuration')">
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
index e8daab11e..41a517826 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/computational/computational-resource-create-dialog/computational-resource-create-dialog.component.ts
@@ -45,6 +45,7 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
   readonly CLUSTER_CONFIGURATION = CLUSTER_CONFIGURATION;
   readonly CheckUtils = CheckUtils;
   readonly providerList: typeof Providers = Providers;
+  readonly templateName: typeof ImageTemplateName = ImageTemplateName;
 
   notebook_instance: any;
   resourcesList: any;
@@ -158,7 +159,7 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
       );
   }
 
-  public isHasHDInside(templateList: ComputationalTemplate[]): boolean {
+  public hasHDInside(templateList: ComputationalTemplate[]): boolean {
     return  templateList.some(({template_name}) => template_name === ImageTemplateName.hdInsight);
   }
 
@@ -392,4 +393,7 @@ export class ComputationalResourceCreateDialogComponent implements OnInit {
   get instanceSpot() {
     return this.resourceForm.controls['emr_slave_instance_spot'].value;
   }
+  get templateNameControl(): FormControl {
+    return  this.resourceForm.get('template_name') as FormControl;
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.html
new file mode 100644
index 000000000..3ca47cf20
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.html
@@ -0,0 +1,61 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+  -->
+
+  <div id="dialog-box" class="dialog__wrapper">
+    <header class="dialog-header">
+      <h4 class="modal-title">
+        {{modalTitle.addPlatform}}
+      </h4>
+      <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+    </header>
+    <div class="dialog-content">
+      <form [formGroup]="connectedPlatformForm">
+          <div class="control__wrapper">
+            <label>Select platform</label>
+            <mat-form-field class="select__wrapper" appearance="fill">
+              <mat-select placeholder="Select platform" name="platformType" formControlName="type">
+                <mat-option *ngIf="!data.types.length" disabled="true">
+                  No platforms to add
+                </mat-option><mat-option *ngFor="let type of data.types" [value]="type">
+                  {{type}}
+                </mat-option>
+              </mat-select>
+              <button class="caret">
+                <i class="material-icons">keyboard_arrow_down</i>
+              </button>
+            </mat-form-field>
+          </div>
+
+        <div class="control__wrapper">
+          <label>Platform url</label>
+          <div class="input__wrapper">
+            <input placeholder="Platform url" type="text" formControlName="url">
+          </div>
+        </div>
+
+        <div class="control__wrapper">
+          <label>Name</label>
+          <div class="input__wrapper">
+            <input placeholder="Enter name" type="text" formControlName="name">
+          </div>
+        </div>
+        <datalab-modal-btn [confirmBtnName]="confirmButtonName.add" (closeEvent)="onBtnClick($event)"></datalab-modal-btn>
+      </form>
+    </div>
+  </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.scss
new file mode 100644
index 000000000..f012798cf
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.scss
@@ -0,0 +1,93 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+.dialog-content {
+  padding: 25px 30px;
+}
+
+.control__wrapper {
+  display: flex;
+  justify-content: space-between;
+  align-items: baseline;
+  margin-bottom: 20px;
+}
+
+.select__wrapper {
+  position: relative;
+  width: 285px;
+  height: 36px;
+
+  & ::ng-deep .mat-form-field-wrapper {
+    padding: 0;
+  }
+
+  &.mat-form-field .mat-form-field-flex {
+    background-color: red !important;
+  }
+
+  & ::ng-deep .mat-form-field-appearance-fill .mat-form-field-flex {
+    padding: 0;
+  }
+
+  & ::ng-deep .mat-form-field-infix {
+    border-top: none;
+  }
+
+  & ::ng-deep .mat-form-field-underline::before {
+    height: 0;
+  }
+
+  & ::ng-deep .mat-form-field-ripple {
+    display: none;
+  }
+
+  & ::ng-deep .mat-select-arrow {
+    border: none;
+
+  }
+
+  ::ng-deep .mat-select-placeholder {
+    font-size: 15px;
+  }
+}
+
+.caret {
+  position: absolute;
+  top: -9px;
+  right: -11px;
+  width: 40px;
+  height: 40px;
+  color: #35afd5;
+  background-color: transparent;
+  border: none;
+  border-left: 1px solid #ececec;
+  outline: none;
+  cursor: pointer;
+}
+
+::ng-deep {
+  .mat-form-field .mat-form-field-flex{
+    background-color: white;
+    box-shadow: 0px 3px 1px -2px rgb(0 0 0 / 20%), 0px 2px 2px 0px rgb(0 0 0 / 14%), 0px 1px 5px 0px rgb(0 0 0 / 12%);
+  }
+}
+
+.input__wrapper {
+  width: 285px;
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.ts
new file mode 100644
index 000000000..9e0f5c458
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platform-dialog/connected-platform-dialog.component.ts
@@ -0,0 +1,63 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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, Inject, OnInit } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { FormBuilder, FormGroup } from '@angular/forms';
+import { ModalTitle } from '../../images';
+import { AddModalData, AddPlatformFromValue } from '../connected-platforms.models';
+import { ConfirmButtonNames } from '../connected-platforms.config';
+
+@Component({
+  selector: 'datalab-connected-platform-dialog',
+  templateUrl: './connected-platform-dialog.component.html',
+  styleUrls: ['./connected-platform-dialog.component.scss']
+})
+export class ConnectedPlatformDialogComponent implements OnInit {
+  readonly modalTitle: typeof ModalTitle = ModalTitle;
+  readonly confirmButtonName: typeof ConfirmButtonNames = ConfirmButtonNames;
+
+  connectedPlatformForm!: FormGroup;
+
+  constructor(
+    public dialogRef: MatDialogRef<ConnectedPlatformDialogComponent>,
+    private fb: FormBuilder,
+    @Inject(MAT_DIALOG_DATA) public data: AddModalData
+  ) { }
+
+  ngOnInit(): void {
+    this.initForm();
+  }
+
+  onBtnClick(flag: boolean): void {
+    let responseObj: AddPlatformFromValue;
+    if (flag) {
+      responseObj = this.connectedPlatformForm.value;
+    }
+    this.dialogRef.close(responseObj);
+  }
+
+  private initForm(): void {
+    this.connectedPlatformForm = this.fb.group({
+      type: '',
+      url: '',
+      name: ''
+    });
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.html
index 7343c83ef..43042832c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.html
@@ -20,6 +20,8 @@
 <section class="connected-platforms__wrapper base-retreat">
   <nav class="sub-nav">
     <button
+      *ngIf="(connectedPlatformsStatus$ | async)?.add"
+      (click)="onAddNew()"
       mat-raised-button
       class="butt butt-create"
     >
@@ -29,27 +31,55 @@
 
   <mat-divider></mat-divider>
 
-  <table mat-table [dataSource]="dataSource" class="mat-elevation-z8 table">
+  <table mat-table [dataSource]="(platformPageData$ | async).userPlatforms" class="mat-elevation-z8 table">
 
     <ng-container matColumnDef="platformName">
       <th mat-header-cell *matHeaderCellDef>{{tableHeaderCellTitles.platformName}}</th>
-      <td mat-cell class="column" *matCellDef="let element"> {{element.platformName}} </td>
+      <td mat-cell class="column" *matCellDef="let element"> {{element.name}} </td>
     </ng-container>
 
     <ng-container matColumnDef="linkToPlatform">
       <th mat-header-cell *matHeaderCellDef>{{tableHeaderCellTitles.linkToPlatform}}</th>
       <td class="column" mat-cell *matCellDef="let element">
-        <a class="link" [href]="element.linkToPlatform | normalizeLink" target="_blank">
-          {{element.linkToPlatform}}
+        <a class="link" [href]="element.url | normalizeLink" target="_blank">
+          {{element.url}}
         </a>
       </td>
     </ng-container>
 
     <ng-container matColumnDef="actions">
-      <th mat-header-cell *matHeaderCellDef>{{tableHeaderCellTitles.actions}}</th>
-      <td mat-cell *matCellDef="let element"><span class="actions">
-        <img class="action-icon" [src]="'assets/svg/settings_icon.svg'" alt="setting-icon">
-      </span></td>
+      <th
+        mat-header-cell
+        [ngClass]="{'hided-table-title': !(connectedPlatformsStatus$ | async)?.disconnect}"
+        *matHeaderCellDef
+      >
+        {{tableHeaderCellTitles.actions}}
+      </th>
+      <td mat-cell class="action-cell" *matCellDef="let element">
+        <span class="actions-menu"
+              #settings
+              (click)="actions.toggle($event, settings)">
+          <img
+            *ngIf="(connectedPlatformsStatus$ | async)?.disconnect"
+            class="action-icon"
+            [src]="'assets/svg/settings_icon.svg'"
+            alt="setting-icon"
+          >
+        </span>
+        <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
+          <ul class="list-unstyled">
+            <li>
+              <button
+                class="action-button__disconnect"
+                (click)="onPlatformDisconnect(element)"
+              >
+                <i class="material-icons icon">cast</i>
+                <span>Disconnect</span>
+              </button>
+            </li>
+          </ul>
+        </bubble-up>
+      </td>
     </ng-container>
 
     <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
index 4bf890f70..d73d9b45f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
@@ -33,3 +33,63 @@
 .link {
   text-decoration: underline;
 }
+
+.hided-table-title {
+  color: transparent;
+}
+
+.table-cell {
+  position: relative;
+}
+
+.list-menu {
+  left: auto;
+  top: 30px !important;
+  width: 190px;
+  max-height: calc(100vh / 2 - 70px);
+  margin-left: 0;
+  box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12);
+  border: none;
+}
+
+.actions-menu {
+  position: relative;
+  width: 190px;
+}
+
+.action-button__disconnect {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  padding: 10px 15px;
+  background-color: transparent;
+  color: rgb(87, 114, 137);
+  border: none;
+  outline: none;
+
+  &:hover {
+    color: #35afd5;
+    cursor: pointer;
+  }
+}
+
+.action-cell {
+  position: relative;
+}
+
+.icon {
+  position: relative;
+  margin-right: 10px;
+
+  &::before {
+    position: absolute;
+    top: 12px;
+    left: -8px;
+    width: 39px;
+    height: 1px;
+    rotate: 40deg;
+    content: '';
+    background-color: black;
+    transform: rotateX(1deg);
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.ts
index 29b815939..16b83a2a9 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.ts
@@ -18,30 +18,18 @@
  */
 
 import { Component, OnInit } from '@angular/core';
-import { GeneralEnvironmentStatus } from '../../administration/management/management.model';
-import { HealthStatusService } from '../../core/services';
+import { BehaviorSubject, EMPTY, Observable, pipe } from 'rxjs';
+import { switchMap, take, tap } from 'rxjs/operators';
+import { MatDialog } from '@angular/material/dialog';
 import { ToastrService } from 'ngx-toastr';
-import { ConnectedPlatformDisplayedColumns, Image_Table_Titles } from './connected-platforms.comnfig';
-
-const mockedData = [
-  {
-    platformName: 'azure',
-    linkToPlatform: 'www.google.com/'
-  },
-  {
-    platformName: 'azure',
-    linkToPlatform: 'google.com'
-  },
-  {
-    platformName: 'azure',
-    linkToPlatform: 'google.com'
-  },
-  {
-    platformName: 'azure',
-    linkToPlatform: 'google.com'
-  },
-];
 
+import { ConnectedPlatformsStatus, GeneralEnvironmentStatus } from '../../administration/management/management.model';
+import { HealthStatusService } from '../../core/services';
+import { ConnectedPlatformsTableTitles, ConnectedPlatformDisplayedColumns } from './connected-platforms.config';
+import { ConnectedPlatformDialogComponent } from './connected-platform-dialog/connected-platform-dialog.component';
+import { ConnectedPlatformsInfo, Platform } from './connected-platforms.models';
+import { ConnectedPlatformsService } from './connected-platforms.service';
+import { WarningDialogComponent } from './warning-dialog/warning-dialog.component';
 
 @Component({
   selector: 'datalab-connected-platforms',
@@ -49,28 +37,77 @@ const mockedData = [
   styleUrls: ['./connected-platforms.component.scss']
 })
 export class ConnectedPlatformsComponent implements OnInit {
-  readonly tableHeaderCellTitles: typeof Image_Table_Titles = Image_Table_Titles;
+  readonly tableHeaderCellTitles: typeof ConnectedPlatformsTableTitles = ConnectedPlatformsTableTitles;
+
+  // tslint:disable-next-line:max-line-length
+  private readonly connectedPlatformsStatus$$: BehaviorSubject<ConnectedPlatformsStatus> = new BehaviorSubject<ConnectedPlatformsStatus>({} as ConnectedPlatformsStatus);
+  readonly connectedPlatformsStatus$: Observable<ConnectedPlatformsStatus> = this.connectedPlatformsStatus$$.asObservable();
 
-  healthStatus: GeneralEnvironmentStatus;
+  platformPageData$: Observable<ConnectedPlatformsInfo>;
 
   displayedColumns: typeof ConnectedPlatformDisplayedColumns = ConnectedPlatformDisplayedColumns;
-  dataSource = mockedData;
 
   constructor(
     private healthStatusService: HealthStatusService,
     public toastr: ToastrService,
+    private dialog: MatDialog,
+    private connectedPlatformsService: ConnectedPlatformsService
   ) { }
 
   ngOnInit(): void {
     this.getEnvironmentHealthStatus();
+    this.getConnectedPlatformPageInfo();
+    this.initPageData();
   }
 
-  private getEnvironmentHealthStatus(): void {
-    this.healthStatusService.getEnvironmentHealthStatus().subscribe(
-      (result: GeneralEnvironmentStatus) => {
-        this.healthStatus = result;
-      },
-      error => this.toastr.error(error.message, 'Oops!')
+  onAddNew(): void {
+    this.dialog.open(ConnectedPlatformDialogComponent, {
+      data: this.connectedPlatformsService.addModalData,
+      panelClass: 'modal-lg'
+    })
+      .afterClosed()
+      .pipe(
+        this.getModalAction(this.connectedPlatformsService.addPlatform),
+      ).subscribe();
+  }
+
+  onPlatformDisconnect({name}: Platform): void {
+    this.dialog.open(WarningDialogComponent,
+      {
+        data: name,
+        panelClass: 'modal-sm'
+      })
+      .afterClosed()
+      .pipe(
+        this.getModalAction(this.connectedPlatformsService.disconnectPlatform)
+      ).subscribe();
+  }
+
+  private getModalAction(callback: Function) {
+    callback = callback.bind(this.connectedPlatformsService);
+    return pipe(
+      switchMap((arg) => {
+        if (arg) {
+          return callback(arg);
+        }
+        return EMPTY;
+      }),
+      switchMap(() => this.connectedPlatformsService.getConnectedPlatformPageInfo())
     );
   }
+
+  private getEnvironmentHealthStatus(): void {
+    this.healthStatusService.getEnvironmentHealthStatus().pipe(
+      tap((response: GeneralEnvironmentStatus) => this.connectedPlatformsStatus$$.next(response.connectedPlatforms)),
+      take(1)
+    ).subscribe();
+  }
+
+  private getConnectedPlatformPageInfo(): void {
+    this.connectedPlatformsService.getConnectedPlatformPageInfo().subscribe();
+  }
+
+  private initPageData(): void {
+    this.platformPageData$ = this.connectedPlatformsService.platformPageData$;
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.config.ts
similarity index 85%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.config.ts
index 0a24005b0..708ff528e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.config.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-export enum Image_Table_Titles {
+export enum ConnectedPlatformsTableTitles {
   platformName = 'Platform name',
   linkToPlatform = 'Link to platform',
   actions = 'Actions'
@@ -28,3 +28,12 @@ export const ConnectedPlatformDisplayedColumns = [
   'linkToPlatform',
   'actions',
 ];
+
+export enum ModalTitles {
+  disconnect= 'Disconnect platform'
+}
+
+export enum ConfirmButtonNames {
+  yes = 'Yes',
+  add = 'Add'
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.models.ts
similarity index 70%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.models.ts
index 4bf890f70..d51487953 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.models.ts
@@ -17,19 +17,19 @@
  * under the License.
  */
 
-.table {
-  width: 100%;
+export interface ConnectedPlatformsInfo {
+  userPlatforms: Platform[];
+  types: string[];
+  platformNames: string[];
 }
 
-.column {
-  width: 20%;
+export interface Platform {
+  name: string;
+  type: string;
+  user: string;
+  url: string;
 }
 
-.action-icon,
-.link {
-  cursor: pointer;
-}
+export type AddModalData = Omit<ConnectedPlatformsInfo, 'userPlatforms'>;
 
-.link {
-  text-decoration: underline;
-}
+export type AddPlatformFromValue = Omit<Platform, 'user'>;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts
index f5ad17999..17fb763b8 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts
@@ -24,17 +24,28 @@ import { ConnectedPlatformsRoutingModule } from './connected-platforms-routing.m
 import { ConnectedPlatformsComponent } from './connected-platforms.component';
 import { MaterialModule } from '../../shared/material.module';
 import { NormalizeLinkPipeModule } from '../../core/pipes';
+import { BubbleModule } from '../../shared';
+import { WarningDialogComponent } from './warning-dialog/warning-dialog.component';
+import { ModalPartsModule } from '../../shared/modal-parts/modal-parts.module';
+import { ConnectedPlatformDialogComponent } from './connected-platform-dialog/connected-platform-dialog.component';
+import { ReactiveFormsModule } from '@angular/forms';
 
 
 @NgModule({
   declarations: [
-    ConnectedPlatformsComponent
+    ConnectedPlatformsComponent,
+    WarningDialogComponent,
+    ConnectedPlatformDialogComponent
   ],
   imports: [
     CommonModule,
     MaterialModule,
     NormalizeLinkPipeModule,
-    ConnectedPlatformsRoutingModule
-  ]
+    ConnectedPlatformsRoutingModule,
+    BubbleModule,
+    ModalPartsModule,
+    ReactiveFormsModule
+  ],
+  entryComponents: [ ConnectedPlatformDialogComponent, WarningDialogComponent ]
 })
 export class ConnectedPlatformsModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.service.ts
new file mode 100644
index 000000000..3a986164a
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.service.ts
@@ -0,0 +1,59 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 { Injectable } from '@angular/core';
+import { ConnectedPlatformApiService } from '../../core/services/connected-platform-api.service';
+import { tap } from 'rxjs/operators';
+import { BehaviorSubject, Observable } from 'rxjs';
+import { AddModalData, AddPlatformFromValue, ConnectedPlatformsInfo } from './connected-platforms.models';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ConnectedPlatformsService {
+  // tslint:disable-next-line:max-line-length
+  private platformPageData$$: BehaviorSubject<ConnectedPlatformsInfo> = new BehaviorSubject<ConnectedPlatformsInfo>({} as ConnectedPlatformsInfo);
+  platformPageData$: Observable<ConnectedPlatformsInfo> = this.platformPageData$$.asObservable();
+  addModalData: AddModalData;
+
+  constructor(
+    private connectedPlatformPageService: ConnectedPlatformApiService
+  ) { }
+
+  getConnectedPlatformPageInfo(): Observable<ConnectedPlatformsInfo> {
+    return this.connectedPlatformPageService.getConnectedPlatformsPage()
+      .pipe(
+        tap( result => this.platformPageData$$.next(result)),
+        tap( result => this.getAddModalData(result)),
+      );
+  }
+
+  addPlatform(platformParams: AddPlatformFromValue): Observable<any> {
+    return this.connectedPlatformPageService.addPlatform(platformParams);
+  }
+
+  disconnectPlatform(platformName: string): Observable<any> {
+    return this.connectedPlatformPageService.disconnectPlatform(platformName);
+  }
+
+  private getAddModalData(info: ConnectedPlatformsInfo): void {
+    const { platformNames, types } = info;
+    this.addModalData = { platformNames, types };
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.html
new file mode 100644
index 000000000..32d382c05
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.html
@@ -0,0 +1,28 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+  -->
+
+<datalab-modal-header (close)="onClose()" [modalTitle]="title.disconnect"></datalab-modal-header>
+<div class="dialog-content">
+  <p class="content red">The <span class="platform-name">{{data}}</span> will be disconnected from this account.</p>
+  <datalab-modal-btn
+    [needAdditionalQuestion]="true"
+    (closeEvent)="onBtnClick($event)"
+    [confirmBtnName]="confirmButtonName.yes"
+  ></datalab-modal-btn>
+</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.scss
similarity index 80%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.scss
index 4bf890f70..310d4b09f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.scss
@@ -17,19 +17,21 @@
  * under the License.
  */
 
-.table {
-  width: 100%;
+.dialog-content {
+  padding: 25px 30px;
 }
 
-.column {
-  width: 20%;
+.red {
+  color: red;
 }
 
-.action-icon,
-.link {
-  cursor: pointer;
+.content {
+  margin-bottom: 50px;
+  padding-top: 20px;
+  text-align: center;
+  font-weight: 400;
 }
 
-.link {
-  text-decoration: underline;
+.platform-name {
+  font-weight: 700;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.ts
new file mode 100644
index 000000000..f30f0d3be
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/warning-dialog/warning-dialog.component.ts
@@ -0,0 +1,49 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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, Inject } from '@angular/core';
+import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { ConfirmButtonNames, ModalTitles } from '../connected-platforms.config';
+
+@Component({
+  selector: 'datalab-warning-dialog',
+  templateUrl: './warning-dialog.component.html',
+  styleUrls: ['./warning-dialog.component.scss']
+})
+export class WarningDialogComponent {
+  readonly title: typeof ModalTitles = ModalTitles;
+  readonly confirmButtonName: typeof ConfirmButtonNames = ConfirmButtonNames;
+
+  constructor(
+    public dialogRef: MatDialogRef<WarningDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: string
+  ) { }
+
+  onClose(): void {
+    this.dialogRef.close();
+  }
+
+  onBtnClick(isConfirm: boolean): void {
+    let platformName;
+    if (isConfirm) {
+      platformName = this.data;
+    }
+    this.dialogRef.close(platformName);
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
index 12321aba0..35559f707 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
@@ -308,7 +308,8 @@
                 </span>
                 <span
                   *ngIf="element.imageUserPermissions | isElementAvailable: isObjectFieldTrue"
-                  #settings class="actions"
+                  class="actions"
+                  #settings
                   (click)="actions.toggle($event, settings)"
                 ></span>
               </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
index 6487cd088..a0301623d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
@@ -78,7 +78,6 @@ export class ImagesComponent implements OnInit, OnDestroy {
   readonly imageActionType: typeof ImageActions = ImageActions;
 
   isActionsOpen: boolean = false;
-  healthStatus: GeneralEnvironmentStatus;
   dataSource: Observable<ImageModel[]>;
   projectSource: Observable<ProjectModel[]>;
   checkboxSelected: boolean = false;
@@ -265,12 +264,7 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   private getEnvironmentHealthStatus(): void {
-    this.healthStatusService.getEnvironmentHealthStatus().subscribe(
-      (result: GeneralEnvironmentStatus) => {
-        this.healthStatus = result;
-      },
-      error => this.toastr.error(error.message, 'Oops!')
-    );
+    this.healthStatusService.getEnvironmentHealthStatus().subscribe();
   }
 
   private initFilteredColumnState(): void {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
index 60a3ab54b..247f47d76 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.config.ts
@@ -122,7 +122,8 @@ export enum ImageActions {
 export enum ModalTitle {
   share = 'Share image',
   terminate = 'Terminate image',
-  unShare = '! Warning'
+  unShare = '! Warning',
+  addPlatform = 'Add platform'
 }
 
 export enum URL_Chunk {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
index b5d74f5fb..a258f5337 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.service.ts
@@ -14,7 +14,7 @@ import {
   ImageActionType,
   ImageActionModalData
 } from './images.model';
-import { ApplicationServiceFacade, UserImagesPageService } from '../../core/services';
+import { ApplicationServiceFacade, ImagesPageService } from '../../core/services';
 import { ChangedColumnStartValue, FilterFormInitialValue, ModalTitle, SharingStatus } from './images.config';
 import { ShareDialogData, UserData } from '../exploratory/image-action-dialog/image-action.model';
 
@@ -45,7 +45,7 @@ export class ImagesService {
 
   constructor(
     private applicationServiceFacade: ApplicationServiceFacade,
-    private userImagesPageService: UserImagesPageService
+    private userImagesPageService: ImagesPageService
   ) { }
 
   getImagePageInfo(): Observable<ProjectImagesInfo> {
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index 6bef1c4e2..fe9561f04 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -32,13 +32,13 @@ import { BucketDataService } from './bucket-browser/bucket-data.service';
 import { ConvertFileSizePipeModule } from '../core/pipes/convert-file-size';
 import { BucketBrowserModule } from './bucket-browser/bucket-browser.module';
 import { ImagesComponent } from './images/images.component';
-import {CheckboxModule} from '../shared/checkbox';
-import {BubbleModule} from '../shared';
+import { CheckboxModule } from '../shared/checkbox';
+import { BubbleModule } from '../shared';
 import { IsElementAvailablePipeModule, NormalizeDropdownMultiValuePipeModule } from '../core/pipes';
 import { LocalDatePipeModule } from '../core/pipes/local-date-pipe';
 import { ImageActionDialogModule } from './exploratory/image-action-dialog/image-action-dialog.module';
 import { ImageDetailDialogModule } from './exploratory/image-detail-dialog/image-detail-dialog.module';
-import {LibraryInfoModalModule} from './exploratory/library-info-modal/library-info-modal.module';
+import { LibraryInfoModalModule } from './exploratory/library-info-modal/library-info-modal.module';
 import { PageFilterComponent } from './exploratory/page-filter/page-filter.component';
 import { DirectivesModule } from '../core/directives';
 
@@ -69,7 +69,6 @@ import { DirectivesModule } from '../core/directives';
     ConfirmDeleteAccountDialogComponent,
     ImagesComponent,
     PageFilterComponent,
-
   ],
   entryComponents: [ManageUngitComponent, ConfirmDeleteAccountDialogComponent],
   providers: [BucketDataService],
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.html
new file mode 100644
index 000000000..0a5089c4b
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.html
@@ -0,0 +1,34 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+  -->
+<div class="component__wrapper">
+  <p *ngIf="needAdditionalQuestion" class="question center">
+    Do you want proceed?
+  </p>
+  <div class="button__wrapper">
+    <button type="button" class="butt mat-raised-button" (click)="close()">No</button>
+    <button
+      [disabled]="isConfirmBtnDisabled"
+      type="button"
+      class="butt butt-success mat-raised-button"
+      (click)="close(true)">
+      {{confirmBtnName}}
+    </button>
+  </div>
+</div>
+
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.scss
similarity index 85%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.scss
index 4bf890f70..d46002dc1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.scss
@@ -1,4 +1,4 @@
-/*!
+/*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -17,19 +17,13 @@
  * under the License.
  */
 
-.table {
-  width: 100%;
+.button__wrapper {
+  text-align: center;
 }
 
-.column {
-  width: 20%;
-}
-
-.action-icon,
-.link {
-  cursor: pointer;
-}
-
-.link {
-  text-decoration: underline;
+.question {
+  margin-bottom: 20px;
+  text-align: center;
+  color: #718ba6;
+  font-weight: bold;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.ts
similarity index 60%
rename from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts
rename to services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.ts
index 0a24005b0..4aa51fb0c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.comnfig.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-btn/modal-btn.component.ts
@@ -17,14 +17,21 @@
  * under the License.
  */
 
-export enum Image_Table_Titles {
-  platformName = 'Platform name',
-  linkToPlatform = 'Link to platform',
-  actions = 'Actions'
-}
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+  selector: 'datalab-modal-btn',
+  templateUrl: './modal-btn.component.html',
+  styleUrls: ['./modal-btn.component.scss']
+})
+export class ModalBtnComponent {
+  @Input() isConfirmBtnDisabled: boolean;
+  @Input() confirmBtnName: string;
+  @Input() needAdditionalQuestion: boolean = false;
 
-export const ConnectedPlatformDisplayedColumns = [
-  'platformName',
-  'linkToPlatform',
-  'actions',
-];
+  @Output() closeEvent: EventEmitter<boolean> = new EventEmitter<boolean>();
+
+  close(flag: boolean = false): void {
+    this.closeEvent.emit(flag);
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.html
new file mode 100644
index 000000000..83650e21b
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.html
@@ -0,0 +1,27 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+  -->
+
+<div>
+  <header class="dialog-header">
+    <h4 class="modal-title">
+      {{modalTitle}}
+    </h4>
+    <button type="button" class="close" (click)="onClose()">&times;</button>
+  </header>
+</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.scss
similarity index 70%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.scss
index 4bf890f70..ece700481 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.scss
@@ -17,19 +17,26 @@
  * under the License.
  */
 
-.table {
-  width: 100%;
+.dialog-header {
+  position: relative;
+  padding-left: 30px;
+  height: 50px;
+  background: #f6fafe;
+  line-height: 50px;
 }
 
-.column {
-  width: 20%;
-}
-
-.action-icon,
-.link {
+.close {
+  position: absolute;
+  top: 0;
+  right: 0;
+  height: 50px;
+  width: 50px;
+  font-size: 24px;
+  font-weight: 300;
+  border: 0;
+  background: none;
+  color: #577289;
+  outline: none;
   cursor: pointer;
-}
-
-.link {
-  text-decoration: underline;
+  transition: all 0.5s ease-in-out;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.ts
similarity index 67%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
copy to services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.ts
index 4bf890f70..7ef9b6d62 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.component.scss
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-header/modal-header.component.ts
@@ -17,19 +17,20 @@
  * under the License.
  */
 
-.table {
-  width: 100%;
-}
+import { Component, EventEmitter, Input, Output } from '@angular/core';
 
-.column {
-  width: 20%;
-}
+@Component({
+  selector: 'datalab-modal-header',
+  templateUrl: './modal-header.component.html',
+  styleUrls: ['./modal-header.component.scss']
+})
+export class ModalHeaderComponent {
+  @Input() modalTitle: string;
 
-.action-icon,
-.link {
-  cursor: pointer;
-}
+  @Output() close: EventEmitter<any> = new EventEmitter<any>();
+
+  onClose(): void {
+    this.close.emit();
+  }
 
-.link {
-  text-decoration: underline;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-parts.module.ts
similarity index 67%
copy from services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-parts.module.ts
index f5ad17999..3559594eb 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/connected-platforms/connected-platforms.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/modal-parts/modal-parts.module.ts
@@ -19,22 +19,22 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
+import { ModalHeaderComponent } from './modal-header/modal-header.component';
+import { ModalBtnComponent } from './modal-btn/modal-btn.component';
 
-import { ConnectedPlatformsRoutingModule } from './connected-platforms-routing.module';
-import { ConnectedPlatformsComponent } from './connected-platforms.component';
-import { MaterialModule } from '../../shared/material.module';
-import { NormalizeLinkPipeModule } from '../../core/pipes';
 
 
 @NgModule({
   declarations: [
-    ConnectedPlatformsComponent
+    ModalHeaderComponent,
+    ModalBtnComponent
   ],
   imports: [
-    CommonModule,
-    MaterialModule,
-    NormalizeLinkPipeModule,
-    ConnectedPlatformsRoutingModule
+    CommonModule
+  ],
+  exports: [
+    ModalBtnComponent,
+    ModalHeaderComponent
   ]
 })
-export class ConnectedPlatformsModule { }
+export class ModalPartsModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
index f724f6786..78ea73c1e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/shared/navbar/navbar.component.html
@@ -129,6 +129,7 @@
             </a>
 
             <a
+              *ngIf="healthStatus.connectedPlatforms?.view"
               class="sub-nav-item"
               [style.margin-left.px]="isExpanded ? '30' : '0'"
               [routerLink]="[routerList.connectedPlatforms]"
diff --git a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
index becd4a2e5..e78e6ba2c 100644
--- a/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
+++ b/services/self-service/src/main/resources/webapp/src/assets/styles/_theme.scss
@@ -147,7 +147,7 @@
 }
 
 .audit {
-  .select-wrapper {
+  .select__wrapper {
     .mat-reset {
       .selector-wrapper {
         height: 25px;
@@ -1056,8 +1056,8 @@ mat-progress-bar {
   }
 }
 .configuration-wrapper {
-  box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 
-              0px 6px 10px 0px rgba(0, 0, 0, 0.14), 
+  box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2),
+              0px 6px 10px 0px rgba(0, 0, 0, 0.14),
               0px 1px 18px 0px rgba(0, 0, 0, 0.12);
 
   .mat-tab-header {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org