Advanced Cluster Management Enhancements

	- Chnaged cluster page design from switch to single page
	- Added bootstrap option in creation and registration
	- Added horizontal scaling, vertical scaling and upgrade in
	  cluster action
	- In cluster page, for list combined two apis to show list done
	  temporary fix from UI

Change-Id: Ie4d30f5db28025ec92398cc60fc50ab7030f8f9b
Signed-off-by: SANDHYA.JS <sandhya.j@tataelxsi.co.in>
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.html b/src/app/k8s/k8scluster/K8sClusterComponent.html
index 0bb251e..3c01f4f 100644
--- a/src/app/k8s/k8scluster/K8sClusterComponent.html
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.html
@@ -16,29 +16,15 @@
 Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
 -->
 <div class="d-flex flex-row justify-content-between">
-  <div class="d-flex align-items-center header-style">
-    <div class="switches-container justify-content-start">
-      <input type="radio" id="switchRegister" name="switchPlan" (change)="onChangeEvent($event.target.value)"
-        value="Registered" checked="checked" />
-      <input type="radio" id="switchManage" name="switchPlan" (change)="onChangeEvent($event.target.value)"
-        value="Managed" />
-      <label for="switchRegister">{{'PAGE.K8S.REGISTERED' | translate}}</label>
-      <label for="switchManage">{{'PAGE.K8S.MANAGED' | translate}}</label>
-      <div class="switch-wrapper">
-        <div class="switch">
-          <div>{{'PAGE.K8S.REGISTERED' | translate}}</div>
-          <div>{{'PAGE.K8S.MANAGED' | translate}}</div>
-        </div>
-      </div>
-    </div>
+  <div class="d-flex align-items-center header-style"> {{'PAGE.K8S.MENUK8SCLUSTER' | translate}}
   </div>
   <span class="button justify-content-end">
-    <button *ngIf="isCluster === 'Registered'" class="btn btn-primary me-2" type="button" placement="top"
-      container="body" ngbTooltip="{{'PAGE.K8S.REGISTERCLUSTER' | translate}}" (click)="addK8sCluster()">
+    <button class="btn btn-primary me-2" type="button" placement="top" container="body"
+      ngbTooltip="{{'PAGE.K8S.REGISTERCLUSTER' | translate}}" (click)="addK8sCluster('Register')">
       <i class="fas fa-plus-circle" aria-hidden="true"></i>&nbsp; {{'PAGE.K8S.REGISTERCLUSTER' | translate}}
     </button>
-    <button *ngIf="isCluster === 'Managed'" class="btn btn-primary me-2" type="button" placement="top" container="body"
-      ngbTooltip="{{'Create Cluster' | translate}}" (click)="addK8sCluster()">
+    <button class="btn btn-primary me-2" type="button" placement="top" container="body"
+      ngbTooltip="{{'PAGE.K8S.CREATECLUSTER' | translate}}" (click)="addK8sCluster('Manage')">
       <i class="fas fa-plus-circle" aria-hidden="true"></i>&nbsp; {{'PAGE.K8S.CREATECLUSTER' | translate}}
     </button>
   </span>
@@ -46,11 +32,14 @@
 <div class="mt-2 mb-2 list-utilites-actions">
   <div class="col-auto me-auto">
     <nav class="custom-items-config">
+      <span><i class="fas fa-cloud-upload-alt text-info"></i>{{clusterModeFirstStep}}</span>
+      <span><i class="fas fa-clipboard-check text-info"></i>{{clusterModeSecondStep}}</span>
       <span><i class="fas fa-clock text-success"></i>{{operationalStateFirstStep}}</span>
       <span><i class="fas fa-spinner text-warning"></i>{{operationalStateSecondStep}}</span>
       <span><i class="fas fa-spinner text-danger"></i>{{operationalStateThirdStep}}</span>
       <span><i class="fas fa-times-circle text-danger"></i>{{operationalStateFourthStep}}</span>
       <span><i class="fas fa-times-circle text-warning"></i>{{operationalStateFifthStep}}</span>
+      <span><i class="fas fa-ban text-danger"></i>{{operationalStateSixthStep}}</span>
     </nav>
   </div>
   <page-per-row class="me-2" (pagePerRow)="onChange($event)"></page-per-row>
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.scss b/src/app/k8s/k8scluster/K8sClusterComponent.scss
index d194553..8c2b739 100644
--- a/src/app/k8s/k8scluster/K8sClusterComponent.scss
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.scss
@@ -15,88 +15,3 @@
 
  Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in)
 */
-.switches-container {
-   border-bottom: 1px solid #5f5f5f;
-   background-color: #054c8c;
-   border-radius: 2px;
-   width: 230px;
-   position: relative;
-   display: block;
-   padding: 0;
-   line-height: 2em;
-   border-radius: 3rem;
-   margin-left: auto;
-   margin-right: auto;
-   height: 36px;
-}
-
-.switches-container input {
-   visibility: hidden;
-   position: absolute;
-   top: 0;
-}
-
-.switches-container label {
-   width: 50%;
-   padding: 0;
-   margin: 0;
-   text-align: center;
-   cursor: pointer;
-   color: white;
-   font-size: 0.875rem;
-   font-weight: 400;
-}
-
-.switch-wrapper {
-   position: absolute;
-   top: 0;
-   bottom: 0;
-   width: 50%;
-   padding: 0.15rem;
-   z-index: 3;
-   transition: transform 0.5s cubic-bezier(0.77, 0, 0.175, 1);
-}
-
-.switch {
-   border-radius: 3rem;
-   background: white;
-   height: 100%;
-   width: 110px;
-   margin-top: 1px;
-   margin-right: 1px;
-}
-
-.switch div {
-   width: 100%;
-   text-align: center;
-   opacity: 0;
-   display: block;
-   color: #054c8c;
-   font-size: 0.875rem;
-   font-weight: 400;
-   transition: opacity 0.2s cubic-bezier(0.77, 0, 0.175, 1) 0.125s;
-   will-change: opacity;
-   position: absolute;
-   top: 0;
-   left: 0;
-}
-
-/* slide the switch box from right to left */
-.switches-container input:nth-of-type(1):checked ~ .switch-wrapper {
-   transform: translateX(0%);
-}
-
-/* slide the switch box from left to right */
-.switches-container input:nth-of-type(2):checked ~ .switch-wrapper {
-   transform: translateX(100%);
-}
-
-/* toggle the switch box labels - first checkbox:checked - show first switch div */
-.switches-container input:nth-of-type(1):checked ~ .switch-wrapper .switch div:nth-of-type(1) {
-   opacity: 1;
-}
-
-/* toggle the switch box labels - second checkbox:checked - show second switch div */
-.switches-container input:nth-of-type(2):checked ~ .switch-wrapper .switch div:nth-of-type(2) {
-   opacity: 1;
-}
diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.ts b/src/app/k8s/k8scluster/K8sClusterComponent.ts
index ee98ed5..95c8e88 100644
--- a/src/app/k8s/k8scluster/K8sClusterComponent.ts
+++ b/src/app/k8s/k8scluster/K8sClusterComponent.ts
@@ -29,8 +29,9 @@
 import { K8SCLUSTERDATA, K8SCLUSTERDATADISPLAY, K8SCREATEDATADISPLAY } from 'K8sModel';
 import { LocalDataSource } from 'ng2-smart-table';
 import { RestService } from 'RestService';
-import { Subscription } from 'rxjs';
-import { SharedService } from 'SharedService';
+import { forkJoin, Observable, Subscription } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { isNullOrUndefined, SharedService } from 'SharedService';
 /**
  * Creating Component
  * @Component takes K8sClusterComponent.html as template url
@@ -81,6 +82,15 @@
   /** operational State failed creation data @public */
   public operationalStateFifthStep: string = CONFIGCONSTANT.k8OperationalStateFifthStep;
 
+  /** operational State failed creation data @public */
+  public operationalStateSixthStep: string = CONFIGCONSTANT.k8OperationalStateSixthStep;
+
+  /** Cluster Mode Managed data @public */
+  public clusterModeFirstStep: string = CONFIGCONSTANT.clusterModeFirstStep;
+
+  /** Cluster Mode Regsitered data @public */
+  public clusterModeSecondStep: string = CONFIGCONSTANT.clusterModeSecondStep;
+
   /** cluster Type @public */
   public isCluster: string = 'Registered';
 
@@ -128,6 +138,32 @@
       name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' },
       identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' },
       version: { title: this.translateService.instant('K8VERSION'), width: '10%' },
+      clusterMode: {
+        title: this.translateService.instant('Cluster Mode'), width: '15%', type: 'html',
+        filter: {
+          type: 'list',
+          config: {
+            selectText: 'Select',
+            list: [
+              { value: this.clusterModeFirstStep, title: this.clusterModeFirstStep },
+              { value: this.clusterModeSecondStep, title: this.clusterModeSecondStep }
+            ]
+          }
+        },
+        valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): string => {
+          if (row.clusterMode === this.clusterModeFirstStep) {
+            return `<span class="icon-label" title="${row.clusterMode}">
+                         <i class="fas fa-cloud-upload-alt text-info"></i>
+                         </span>`;
+          } else if (row.clusterMode === this.clusterModeSecondStep) {
+            return `<span class="icon-label" title="${row.clusterMode}">
+                         <i class="fas fa-clipboard-check text-info"></i>
+                         </span>`;
+          } else {
+            return `<span>${row.clusterMode}</span>`;
+          }
+        }
+      },
       state: {
         title: this.translateService.instant('STATE'), width: '15%', type: 'html',
         filter: {
@@ -138,8 +174,9 @@
               { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep },
               { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep },
               { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep },
-              { value: this.operationalStateThirdStep, title: this.operationalStateFourthStep },
-              { value: this.operationalStateThirdStep, title: this.operationalStateFifthStep }
+              { value: this.operationalStateFourthStep, title: this.operationalStateFourthStep },
+              { value: this.operationalStateFifthStep, title: this.operationalStateFifthStep },
+              { value: this.operationalStateSixthStep, title: this.operationalStateFifthStep }
             ]
           }
         },
@@ -164,13 +201,16 @@
             return `<span class="icon-label" title="${row.state}">
                          <i class="fas fa-times-circle text-warning"></i>
                          </span>`;
+          } else if (row.state === this.operationalStateSixthStep) {
+            return `<span class="icon-label" title="${row.state}">
+                         <i class="fas fa-ban text-danger"></i>
+                         </span>`;
           } else {
             return `<span>${row.state}</span>`;
           }
         }
       },
       created: { title: this.translateService.instant('CREATED'), width: '15%' },
-      modified: { title: this.translateService.instant('MODIFIED'), width: '15%' },
       Actions: {
         name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom',
         valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): K8SCLUSTERDATADISPLAY => row,
@@ -202,11 +242,11 @@
   }
 
   /** Compose new K8s Cluster Accounts @public */
-  public addK8sCluster(): void {
+  public addK8sCluster(type?: string): void {
     // eslint-disable-next-line security/detect-non-literal-fs-filename
     const modalRef: NgbModalRef = this.modalService.open(K8sAddClusterComponent, { backdrop: 'static' });
 
-    if (this.isCluster === 'Registered') {
+    if (type === 'Register') {
       modalRef.componentInstance.profileType = 'Register';
     } else {
       modalRef.componentInstance.profileType = 'Manage';
@@ -231,56 +271,83 @@
   public generateK8sclusterData(k8sClusterdata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY {
     return {
       name: k8sClusterdata.name,
-      state: k8sClusterdata.state,
+      state: !isNullOrUndefined(k8sClusterdata.state) ? k8sClusterdata.state : 'N/A',
       identifier: k8sClusterdata._id,
-      operationalState: k8sClusterdata._admin.operationalState,
+      operationalState: !isNullOrUndefined(k8sClusterdata._admin.operationalState) ? k8sClusterdata._admin.operationalState : 'N/A',
       version: k8sClusterdata.k8s_version,
       created: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.created)),
       modified: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.modified)),
-      pageType: 'cluster'
+      pageType: 'cluster',
+      createdbyosm: !isNullOrUndefined(k8sClusterdata.created) ? (k8sClusterdata.created) : 'false',
+      bootstrap: !isNullOrUndefined(k8sClusterdata.bootstrap) ? (k8sClusterdata.bootstrap) : false,
+      key: (k8sClusterdata.key === 'registered') ? true : false,
+      clusterMode: (k8sClusterdata.created === 'true') ? 'MANAGED' : 'REGISTERED'
     };
   }
 
-  /** Change event @public */
-  public onChangeEvent(value: string): void {
-    if (value === 'Managed') {
-      this.isCluster = 'Managed';
-    } else {
-      this.isCluster = 'Registered';
-    }
-    sessionStorage.setItem('clusterType', value);
-    this.generateColumns();
-    this.generateSettings();
-    this.generateData();
-  }
-
   /** Fetching the data from server to Load in the smarttable @protected */
   protected generateData(): void {
     this.isLoadingResults = true;
-    if (this.isCluster === 'Registered') {
-      this.clusterUrl = environment.K8SCREATECLUSTER_URL + '?created=false';
-    } else {
-      this.clusterUrl = environment.K8SCREATECLUSTER_URL + '?created=true';
-    }
-    this.restService.getResource(this.clusterUrl).subscribe((k8sClusterDatas: K8SCLUSTERDATA[]) => {
-      this.k8sClusterData = [];
-      k8sClusterDatas.forEach((k8sClusterdata: K8SCLUSTERDATA) => {
-        const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateK8sclusterData(k8sClusterdata);
-        this.k8sClusterData.push(k8sClusterDataObj);
+    const tempURL: Observable<K8SCLUSTERDATA[]>[] = [];
+    const apiUrl1: string = environment.K8SCLUSTER_URL;
+    const apiUrl2: string = environment.K8SCREATECLUSTER_URL;
+    const response1$: Observable<K8SCLUSTERDATA[]> = this.restService.getResource(apiUrl1).pipe(
+      map((data: unknown) =>
+        data as K8SCLUSTERDATA[]
+      )
+    );
+    const response2$: Observable<K8SCLUSTERDATA[]> = this.restService.getResource(apiUrl2).pipe(
+      map((data: unknown) =>
+        data as K8SCLUSTERDATA[]
+      )
+    );
+    tempURL.push(response1$, response2$);
+    forkJoin(tempURL).subscribe(
+      ([response1, response2]) => {
+        if (!isNullOrUndefined(response1) && !isNullOrUndefined(response2)) {
+          const combinedResponse: K8SCLUSTERDATA[] = [...response1, ...response2];
+          const uniqueResponse = this.removeDuplicates(combinedResponse);
+          this.k8sClusterData = [];
+          uniqueResponse.forEach((clusterData: K8SCLUSTERDATA) => {
+            const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateK8sclusterData(clusterData);
+            this.k8sClusterData.push(k8sClusterDataObj);
+          });
+          if (this.k8sClusterData.length > 0) {
+            this.checkDataClass = 'dataTables_present';
+          } else {
+            this.checkDataClass = 'dataTables_empty';
+          }
+          this.dataSource.load(this.k8sClusterData).then((data: boolean) => {
+            this.isLoadingResults = false;
+          }).catch(() => {
+            this.isLoadingResults = false;
+          });
+        }
+      }, (error: ERRORDATA) => {
+        this.restService.handleError(error, 'get');
+        this.isLoadingResults = false;
       });
-      if (this.k8sClusterData.length > 0) {
-        this.checkDataClass = 'dataTables_present';
+  }
+
+  /** Removes duplicates based on 'name' and merges them if necessary */
+  private removeDuplicates(response: K8SCLUSTERDATA[]): K8SCLUSTERDATA[] {
+    const seenIds = new Set();
+    const uniqueResponse: K8SCLUSTERDATA[] = [];
+    response.forEach((obj) => {
+      if (!seenIds.has(obj.name)) {
+        uniqueResponse.push(obj);
+        seenIds.add(obj.name);
       } else {
-        this.checkDataClass = 'dataTables_empty';
+        const existingObj = uniqueResponse.find((o) => o.name === obj.name);
+        Object.keys(obj).forEach((key) => {
+          // eslint-disable-next-line security/detect-object-injection
+          if (existingObj[key] !== obj[key]) {
+            // eslint-disable-next-line security/detect-object-injection
+            existingObj[key] = obj[key];
+          }
+        });
       }
-      this.dataSource.load(this.k8sClusterData).then((data: boolean) => {
-        this.isLoadingResults = false;
-      }).catch(() => {
-        this.isLoadingResults = false;
-      });
-    }, (error: ERRORDATA) => {
-      this.restService.handleError(error, 'get');
-      this.isLoadingResults = false;
     });
+    return uniqueResponse;
   }
 }