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

hshpak pushed a commit to branch 
feat/DATALAB-2881/filter-function-to-Images-page
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 1a671cc15710768a92d8a93df1239640c114a21e
Author: Hennadii_Shpak <[email protected]>
AuthorDate: Sat Jul 30 10:06:19 2022 +0300

    filter list reload implemented
---
 .../project/project-list/project-list.component.ts |   4 +-
 .../directives/is-endpoint-active.directive.ts     |   4 +-
 .../resources/webapp/src/app/core/pipes/index.ts   |   2 +-
 .../index.ts                                       |   6 +-
 .../normalize-dropdown-multi-value.pipe.ts}        |  17 +-
 .../services/applicationServiceFacade.service.ts   |   8 +-
 .../app/core/services/image-page-resolve.guard.ts  |   2 +-
 .../app/core/services/user-images-page.service.ts  |  11 +-
 .../case-insensitive-sort-util.ts}                 |   9 +-
 ...EndpointList.ts => check-endpoint-list-util.ts} |   2 +-
 .../resources/webapp/src/app/core/util/index.ts    |   4 +-
 .../{pipes/index.ts => util/to-title-case-util.ts} |  11 +-
 .../image-detail-dialog.component.ts               |  12 +-
 .../page-filter/page-filter.component.html         |  30 ++-
 .../page-filter/page-filter.component.scss         |   4 +
 .../page-filter/page-filter.component.ts           |  57 ++--
 .../src/app/resources/images/images.component.html | 298 ++++++++++++---------
 .../src/app/resources/images/images.component.scss | 111 +++++++-
 .../src/app/resources/images/images.component.ts   |  60 +++--
 .../src/app/resources/images/images.config.ts      |  25 +-
 .../src/app/resources/images/images.model.ts       |  22 +-
 .../src/app/resources/images/images.service.ts     |  66 ++++-
 .../resources-grid/resources-grid.component.ts     |   1 +
 23 files changed, 538 insertions(+), 228 deletions(-)

diff --git 
a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
 
b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
index 059758be3..9800bfe5e 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/administration/project/project-list/project-list.component.ts
@@ -28,7 +28,7 @@ import {ProgressBarService} from 
'../../../core/services/progress-bar.service';
 import {EdgeActionDialogComponent} from 
'../../../shared/modal-dialog/edge-action-dialog';
 import { EndpointService } from '../../../core/services';
 import { Endpoint, ModifiedEndpoint, Project } from '../project.model';
-import { checkEndpointList } from '../../../core/util';
+import { checkEndpointListUtil } from '../../../core/util';
 import { EndpointStatus } from '../project.config';
 
 @Component({
@@ -110,7 +110,7 @@ export class ProjectListComponent implements OnInit, 
OnDestroy {
   }
 
   isRecreateBtnDisabled(endpointList: ModifiedEndpoint[]): boolean {
-    return checkEndpointList(endpointList);
+    return checkEndpointListUtil(endpointList);
   }
 
   private getFilteredEndpointList(action: string, project) {
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/directives/is-endpoint-active.directive.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/is-endpoint-active.directive.ts
index 0f9db68bd..60c9beedd 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/directives/is-endpoint-active.directive.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/directives/is-endpoint-active.directive.ts
@@ -20,7 +20,7 @@
 import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from 
'@angular/core';
 
 import { ModifiedEndpoint } from '../../administration/project/project.model';
-import { checkEndpointList } from '../util';
+import { checkEndpointListUtil } from '../util';
 
 
 @Directive({
@@ -55,6 +55,6 @@ export class IsEndpointsActiveDirective implements OnInit {
   }
 
   private checkEndpointList(endpointList: ModifiedEndpoint[]): void {
-    this.isButtonDisabled = checkEndpointList(endpointList);
+    this.isButtonDisabled = checkEndpointListUtil(endpointList);
   }
 }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
index bc2e2c7b6..b4a481e95 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
@@ -23,4 +23,4 @@ export * from './lib-sort-pipe';
 export * from './replace-breaks-pipe';
 export * from './highlight.pipe';
 export * from './convert-action-pipe';
-export * from './capitalize-first-letter-pipe';
+export * from './normalize-dropdown-multi-value';
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/index.ts
similarity index 84%
rename from 
services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
rename to 
services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/index.ts
index d333ad504..d8b409ce1 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/index.ts
@@ -19,12 +19,12 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { CapitalizeFirstLetterPipe } from './capitalize-first-letter.pipe';
+import { NormalizeDropdownMultiValuePipe } from 
'./normalize-dropdown-multi-value.pipe';
 
 @NgModule({
   imports: [CommonModule],
-  declarations: [CapitalizeFirstLetterPipe],
-  exports: [CapitalizeFirstLetterPipe]
+  declarations: [NormalizeDropdownMultiValuePipe],
+  exports: [NormalizeDropdownMultiValuePipe]
 })
 
 export class CapitalizeFirstLetterPipeModule { }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/normalize-dropdown-multi-value.pipe.ts
similarity index 72%
rename from 
services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
rename to 
services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/normalize-dropdown-multi-value.pipe.ts
index df57fee83..2a81a4ef5 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/capitalize-first-letter-pipe/capitalize-first-letter.pipe.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/pipes/normalize-dropdown-multi-value/normalize-dropdown-multi-value.pipe.ts
@@ -17,17 +17,20 @@
  * under the License.
  */
 
-
 import { Pipe, PipeTransform } from '@angular/core';
 
-@Pipe({ name: 'capitalizeFirstLetter' })
+@Pipe({ name: 'normalizeDropdownMultiValue' })
 
-export class CapitalizeFirstLetterPipe implements PipeTransform {
-  transform(value: string): string {
-    if (!value) {
+export class NormalizeDropdownMultiValuePipe implements PipeTransform {
+  transform(value: string[]): string {
+    if (!value.length) {
       return '';
     }
-    const firstLetter = value. substring(0, 1). toUpperCase();
-    return `${firstLetter}${value.substring(1).toLowerCase()}`;
+    const [firstValue] = value;
+    if (value.length === 1) {
+      return firstValue;
+    }
+
+    return `${firstValue} (+${value.length - 1} others)`;
   }
 }
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 f97ca1da1..5f85bb5fa 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,7 @@ import { HttpClient } from '@angular/common/http';
 import { Dictionary } from '../collections';
 import { environment } from '../../../environments/environment';
 import { HTTPMethod } from '../util';
-import { ShareImageAllUsersParams } from '../../resources/images';
+import { ImageFilterFormValue, ShareImageAllUsersParams} from 
'../../resources/images';
 
 // we can now access environment.apiUrl
 const API_URL = environment.apiUrl;
@@ -189,6 +189,12 @@ export class ApplicationServiceFacade {
       null);
   }
 
+  buildFilterUserImagePage(params: ImageFilterFormValue): Observable<any> {
+    return this.buildRequest(HTTPMethod.POST,
+      this.requestRegistry.Item(ApplicationServiceFacade.IMAGE_PAGE),
+      params);
+  }
+
   buildShareImageAllUsers(params: ShareImageAllUsersParams): Observable<any> {
     return this.buildRequest(HTTPMethod.POST,
       this.requestRegistry.Item(ApplicationServiceFacade.SHARE_ALL),
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 77df3956f..4f89ede57 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
@@ -16,7 +16,7 @@ export class ImagePageResolveGuard implements 
Resolve<ProjectModel[]> {
   ) {}
 
   resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): 
Observable<ProjectModel[]> {
-    return this.imagesService.getUserImagePageInfo().pipe(
+    return this.imagesService.getImagePageInfo().pipe(
       switchMap((projectList: ProjectModel[]) => of(projectList)),
       take(1)
     );
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/user-images-page.service.ts
index 9f9e0afff..198e8c8ee 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/user-images-page.service.ts
@@ -23,7 +23,7 @@ import {  catchError } from 'rxjs/operators';
 import {  ErrorUtils } from '../util';
 
 import { ApplicationServiceFacade } from './applicationServiceFacade.service';
-import {ProjectModel, ShareImageAllUsersParams} from '../../resources/images';
+import {ImageFilterFormDropdownData, ImageFilterFormValue, ProjectModel, 
ShareImageAllUsersParams} from '../../resources/images';
 
 @Injectable()
 export class UserImagesPageService {
@@ -32,13 +32,20 @@ export class UserImagesPageService {
   ) { }
 
 
-  getUserImagePageInfo(): Observable<ProjectModel[]> {
+  getFilterImagePage(): Observable<ProjectModel[]> {
     return this.applicationServiceFacade.buildGetUserImagePage()
       .pipe(
         catchError(ErrorUtils.handleServiceError)
       );
   }
 
+  filterImagePage(params: ImageFilterFormValue): Observable<ProjectModel[]> {
+    return this.applicationServiceFacade.buildFilterUserImagePage(params)
+      .pipe(
+        catchError(ErrorUtils.handleServiceError)
+      );
+  }
+
   shareImagesAllUser(shareParams: ShareImageAllUsersParams): 
Observable<ProjectModel[]> {
     return this.applicationServiceFacade.buildShareImageAllUsers(shareParams)
       .pipe(
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts 
b/services/self-service/src/main/resources/webapp/src/app/core/util/case-insensitive-sort-util.ts
similarity index 75%
copy from 
services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
copy to 
services/self-service/src/main/resources/webapp/src/app/core/util/case-insensitive-sort-util.ts
index bc2e2c7b6..416d6cca2 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/util/case-insensitive-sort-util.ts
@@ -17,10 +17,5 @@
  * under the License.
  */
 
-export * from './keys-pipe';
-export * from './underscoreless-pipe';
-export * from './lib-sort-pipe';
-export * from './replace-breaks-pipe';
-export * from './highlight.pipe';
-export * from './convert-action-pipe';
-export * from './capitalize-first-letter-pipe';
+
+export const caseInsensitiveSortUtil = (arr: string[]): string[] => 
arr.sort(((a, b) => a.toLowerCase() > b.toLowerCase() ? 1 : -1));
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/util/checkEndpointList.ts
 
b/services/self-service/src/main/resources/webapp/src/app/core/util/check-endpoint-list-util.ts
similarity index 93%
rename from 
services/self-service/src/main/resources/webapp/src/app/core/util/checkEndpointList.ts
rename to 
services/self-service/src/main/resources/webapp/src/app/core/util/check-endpoint-list-util.ts
index 4e2e0bfd9..b8aa14a74 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/util/checkEndpointList.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/util/check-endpoint-list-util.ts
@@ -19,7 +19,7 @@
 
 import { ModifiedEndpoint } from '../../administration/project/project.model';
 
-export const checkEndpointList = (endpointList: ModifiedEndpoint[]): boolean 
=> {
+export const checkEndpointListUtil = (endpointList: ModifiedEndpoint[]): 
boolean => {
   const isAllInactiveEdgeNodeHaveInactiveEndpoint =  
endpointList.filter(({status}) => status === 'TERMINATED' || status === 
'FAILED')
     .every(({endpointStatus}) => !endpointStatus || endpointStatus === 
'INACTIVE');
   return isAllInactiveEdgeNodeHaveInactiveEndpoint;
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/util/index.ts 
b/services/self-service/src/main/resources/webapp/src/app/core/util/index.ts
index b00239022..b15943474 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/util/index.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/index.ts
@@ -26,4 +26,6 @@ export * from './fileUtils';
 export * from './checkUtils';
 export * from './patterns';
 export * from './http-methods';
-export * from './checkEndpointList';
+export * from './check-endpoint-list-util';
+export * from './case-insensitive-sort-util';
+export * from './to-title-case-util';
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts 
b/services/self-service/src/main/resources/webapp/src/app/core/util/to-title-case-util.ts
similarity index 75%
copy from 
services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
copy to 
services/self-service/src/main/resources/webapp/src/app/core/util/to-title-case-util.ts
index bc2e2c7b6..42448bb11 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/core/pipes/index.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/core/util/to-title-case-util.ts
@@ -17,10 +17,7 @@
  * under the License.
  */
 
-export * from './keys-pipe';
-export * from './underscoreless-pipe';
-export * from './lib-sort-pipe';
-export * from './replace-breaks-pipe';
-export * from './highlight.pipe';
-export * from './convert-action-pipe';
-export * from './capitalize-first-letter-pipe';
+export const toTitleCaseUtil = (value: string): string => {
+  const firstLetter = value.substring(0, 1).toUpperCase();
+  return `${firstLetter}${value.substring(1).toLowerCase()}`;
+};
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
index 1894f5763..61c8ad117 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
@@ -19,9 +19,9 @@
 
 import { Component, Inject, OnInit } from '@angular/core';
 import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from 
'@angular/material/dialog';
-import {Library, ModalData} from '../../images';
+import {LibraryInfoItem, Library, ModalData} from '../../images';
 import {LibraryInfoModalComponent} from 
'../library-info-modal/library-info-modal.component';
-
+import {caseInsensitiveSortUtil} from '../../../core/util';
 
 @Component({
   selector: 'datalab-image-detail-dialog',
@@ -42,7 +42,7 @@ export class ImageDetailDialogComponent implements OnInit {
     private dialog: MatDialog,
   ) { }
 
-  ngOnInit() {
+  ngOnInit(): void {
     this.libraryList = this.normalizeLibraries();
   }
 
@@ -55,12 +55,12 @@ export class ImageDetailDialogComponent implements OnInit {
     });
   }
 
-  private normalizeLibraries() {
+  private normalizeLibraries(): LibraryInfoItem[] {
     return this.data.image.libraries.reduce((acc, item) => {
       const libraryName = this.normalizeLibraryName(item);
       const isLibAdded = acc.find(({name}) => item.group === name);
       if (!isLibAdded) {
-        const newLibrary = {
+        const newLibrary: LibraryInfoItem = {
           name: item.group,
           libs: [`${libraryName} v ${item.version}`]
         };
@@ -71,7 +71,7 @@ export class ImageDetailDialogComponent implements OnInit {
       return acc;
     }, [])
       .map(item => {
-        item.libs.sort((a, b) => a.toLowerCase() > b.toLowerCase() ? 1 : -1);
+        caseInsensitiveSortUtil(item.libs);
         return item;
     });
   }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
index 066ee44b6..2782e7f41 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.html
@@ -22,11 +22,16 @@
     <form class="filter-table__wrapper" [formGroup]="filterForm">
       <div class="form-control__wrapper control-group">
         <label class="label">Custom tag</label>
-        <input type="text" class="form-control" 
[placeholder]="placeholders.imageName" formControlName="imageName"
-               matInput
-               [matAutocomplete]="auto">
+        <input
+          type="text"
+          class="form-control"
+          [placeholder]="placeholders.imageName"
+          [formControlName]="dropdownFieldNames.imageName"
+          matInput
+          [matAutocomplete]="auto"
+        />
         <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
-          <mat-option *ngFor="let option of ($filterDropdownData | 
async).imageNames" [value]="option">
+          <mat-option *ngFor="let option of ($filterDropdownData | 
async).imageName" [value]="option">
             {{option}}
           </mat-option>
         </mat-autocomplete>
@@ -38,18 +43,21 @@
             <span class="form-field-wrapper">
               <mat-form-field>
                 <mat-select
-                  formControlName="statuses"
+                  [formControlName]="dropdownFieldNames.statuses"
                   disableOptionCentering
                   panelClass="create-resources-dialog scrolling"
                   [placeholder]="placeholders.status"
                   multiple
                   (click)="onSelectClick()"
                 >
+                  <mat-select-trigger class="select__value">
+                    {{statuses.value | normalizeDropdownMultiValue | 
titlecase}}
+                  </mat-select-trigger>
                   <mat-option
                     *ngFor="let status of ($filterDropdownData | 
async).imageStatuses"
                     [value]="status"
                   >
-                    {{ status }}
+                    {{ status | titlecase }}
                   </mat-option>
                 </mat-select>
                 <button class="caret">
@@ -66,13 +74,16 @@
             <span class="form-field-wrapper">
               <mat-form-field>
                 <mat-select
-                  formControlName="cloudProviders"
+                  [formControlName]="dropdownFieldNames.endpoints"
                   disableOptionCentering
                   panelClass="create-resources-dialog scrolling"
                   [placeholder]="placeholders.endpoint"
                   multiple
                   (click)="onSelectClick()"
                 >
+                  <mat-select-trigger class="select__value">
+                    {{endpoints.value | normalizeDropdownMultiValue}}
+                  </mat-select-trigger>
                   <mat-option
                     *ngFor="let endpoint of ($filterDropdownData | 
async).endpoints"
                     [value]="endpoint"
@@ -94,13 +105,16 @@
             <span class="form-field-wrapper">
               <mat-form-field>
                 <mat-select
-                  formControlName="templateNames"
+                  [formControlName]="dropdownFieldNames.templateNames"
                   disableOptionCentering
                   panelClass="create-resources-dialog scrolling"
                   [placeholder]="placeholders.templateName"
                   multiple
                   (click)="onSelectClick()"
                 >
+                  <mat-select-trigger class="select__value">
+                    {{templateNames.value | normalizeDropdownMultiValue}}
+                  </mat-select-trigger>
                   <mat-option
                     *ngFor="let template of ($filterDropdownData | 
async).templateNames"
                     [value]="template"
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.scss
 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.scss
index 1b6956150..b578cd45d 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.scss
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.scss
@@ -53,3 +53,7 @@
 .content-box {
   padding: 25px 0 35px;
 }
+
+.select__value {
+  color: #607d8b;
+}
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
index edc9d4ce7..10184ea2b 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/page-filter/page-filter.component.ts
@@ -17,13 +17,13 @@
  * under the License.
  */
 
-import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
-import {AbstractControl, FormBuilder, FormControl, FormGroup} from 
'@angular/forms';
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
 
-import {FilterFormPlaceholders} from './page-filter.config';
-import {DropdownFieldNames, FilterDropdownValue} from '../../images';
-import {Observable} from 'rxjs';
-import {tap} from 'rxjs/operators';
+import { FilterFormPlaceholders } from './page-filter.config';
+import { DropdownFieldNames, ImageFilterFormDropdownData, ImageFilterFormValue 
} from '../../images';
 
 @Component({
   selector: 'datalab-page-filter',
@@ -31,14 +31,16 @@ import {tap} from 'rxjs/operators';
   styleUrls: ['./page-filter.component.scss']
 })
 export class PageFilterComponent implements OnInit {
-  @Input() $filterDropdownData: Observable<FilterDropdownValue>;
+  @Input() $filterDropdownData: Observable<ImageFilterFormDropdownData>;
+  @Input() $filterFormStartValue: Observable<ImageFilterFormValue>;
 
-  @Output() filterFormValue: EventEmitter<FilterDropdownValue> = new 
EventEmitter<FilterDropdownValue>();
+  @Output() filterFormValue: EventEmitter<ImageFilterFormValue> = new 
EventEmitter<ImageFilterFormValue>();
   @Output() closeFilter: EventEmitter<any> = new EventEmitter<any>();
   @Output() imageNameValue: EventEmitter<string> = new EventEmitter<string>();
   @Output() onValueChanges: EventEmitter<string> = new EventEmitter<string>();
 
   readonly placeholders: typeof FilterFormPlaceholders = 
FilterFormPlaceholders;
+  readonly dropdownFieldNames: typeof DropdownFieldNames = DropdownFieldNames;
 
   filterForm: FormGroup;
 
@@ -47,17 +49,9 @@ export class PageFilterComponent implements OnInit {
   ) { }
 
   ngOnInit(): void {
-    this.initFilterForm();
+    this.createFilterForm();
     this.onControlChange(DropdownFieldNames.imageName);
-  }
-
-  initFilterForm(): void {
-    this.filterForm = this.fb.group({
-      imageName: '',
-      cloudProviders: [[]],
-      statuses: [[]],
-      templateNames: [[]]
-    });
+    this.setFilterValue();
   }
 
   onSelectClick(): void {
@@ -73,11 +67,34 @@ export class PageFilterComponent implements OnInit {
     this.closeFilter.emit();
   }
 
-  onControlChange(fieldName: keyof FilterDropdownValue): void {
-      console.log(this.filterForm.get(fieldName));
+  onControlChange(fieldName: keyof ImageFilterFormDropdownData): void {
    this.filterForm.get(fieldName)?.valueChanges.pipe(
       tap((inputValue: string) => this.onValueChanges.emit(inputValue))
     ).subscribe();
+  }
+
+  private createFilterForm(): void {
+    this.filterForm = this.fb.group({
+      imageName: '',
+      endpoints: [[]],
+      statuses: [[]],
+      templateNames: [[]]
+    });
+  }
+
+  private setFilterValue(): void {
+    this.$filterFormStartValue.subscribe(value => 
this.filterForm.patchValue(value));
+  }
+
+  get statuses() {
+    return this.filterForm.get(DropdownFieldNames.statuses) as FormControl;
+  }
+
+  get endpoints() {
+    return this.filterForm.get(DropdownFieldNames.endpoints) as FormControl;
+  }
 
+  get templateNames() {
+    return this.filterForm.get(DropdownFieldNames.templateNames) as 
FormControl;
   }
 }
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 49a106309..44f9fad82 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
@@ -60,6 +60,8 @@
     </div>
 
     <div class="button--wrapper">
+
+
       <span class="action-button--wrapper">
           <button
             type="button"
@@ -91,19 +93,38 @@
           </button>
         </div>
       </span>
+
       <button mat-raised-button [disabled]="!(dataSource | async)?.length" 
class="butt filter__btn" (click)="onFilterClick()">
         <i class="material-icons">filter_list</i>
         <span class="filter__btn--name">Filter</span>
+        <button *ngIf="isFiltered" type="button" 
(click)="onResetFilterClick($event)" class="close__btn">&times;</button>
       </button>
       <div *ngIf="isFilterOpened | async" class="filer__wrapper">
         <datalab-page-filter
           [$filterDropdownData]="$filterDropdownData"
+          [$filterFormStartValue]="$filterFormValue"
           (filterFormValue)="onFilterApplyClick($event)"
           (closeFilter)="onFilterCancelClick()"
-          (onValueChanges)="onControlChanges(dropdownFieldNames.imageNames, 
$event)"
+          (onValueChanges)="onControlChanges(dropdownFieldNames.imageName, 
$event)"
         >
         </datalab-page-filter>
       </div>
+
+      <button
+        mat-raised-button
+        class="butt mr-10 show-active__btn"
+        (click)="toggleShowActive()"
+      >
+        <span *ngIf="isShowActive; else inactive">
+          <i class="material-icons">visibility_off</i> Show active
+        </span>
+        <ng-template #inactive>
+          <span>
+            <i class="material-icons">visibility</i> Show all
+          </span>
+        </ng-template>
+      </button>
+
       <span>
         <button mat-raised-button class="butt" (click)="onRefreshClick()">
           <i class="material-icons highlight">autorenew</i>
@@ -115,10 +136,21 @@
 
   <mat-divider></mat-divider>
 
-  <table mat-table [dataSource]="dataSource | async" class="mat-elevation-z8 
image-table data-grid">
-    <ng-container matColumnDef="checkbox">
-      <th mat-header-cell *matHeaderCellDef class="image-checkbox--wrapper">
-        <div class="header-cell--wrapper">
+    <table
+      mat-table
+      [dataSource]="projectSource | async"
+      multiTemplateDataRows
+      class="data-grid resources mat-elevation-z6"
+      [trackBy]="trackBy"
+    >
+
+      <ng-container matColumnDef="project">
+        <td mat-cell *matCellDef="let element" [attr.colspan]="8" 
class="image-page__project"> {{ element.project }} </td>
+      </ng-container>
+
+      <ng-container matColumnDef="checkbox">
+        <th mat-header-cell *matHeaderCellDef class="image-checkbox--wrapper">
+          <div class="header-cell--wrapper">
           <span  *ngIf="(dataSource | async)?.length" >
             <datalab-checkbox
               (click)="allCheckboxToggle()"
@@ -126,130 +158,142 @@
               class="image-checkbox"
             ></datalab-checkbox>
           </span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element" class="image-checkbox--wrapper">
-        <datalab-checkbox
-          (click)="onCheckboxClick(element)"
-          class="image-checkbox"
-          [checked]="element.isSelected"
-        ></datalab-checkbox>
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="imageName">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.imageName}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element"> {{element.name}} </td>
-    </ng-container>
-
-    <ng-container matColumnDef="imageStatus">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.imageStatus}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element" ngClass="{{ 
element.status.toLowerCase() || ''}}">
-        {{element.status | capitalizeFirstLetter}}
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="creationDate">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.creationDate}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element">
-        <span> {{element.timestamp | localDate : 'short'}} </span>
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="endpoint">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.endpoint}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element"> {{element.endpoint}} </td>
-    </ng-container>
-
-    <ng-container matColumnDef="templateName">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.templateName}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element"> {{element.templateName}} </td>
-    </ng-container>
-
-    <ng-container matColumnDef="sharedStatus">
-      <th mat-header-cell *matHeaderCellDef>
-        <div class="header-cell--wrapper">
-          <span>{{tableHeaderCellTitles.sharedStatus}}</span>
-        </div>
-      </th>
-      <td mat-cell *matCellDef="let element">
-        <div class="shared-status--wrapper">
-          <span class="shared-status"> {{element.shared ? sharedStatus.shared 
: sharedStatus.private}} </span>
-          <span *ngIf="element.shared" class="currency_details" >
+          </div>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="imageName">
+        <th mat-header-cell *matHeaderCellDef class="name-col header-cell">
+          <span class="label image-label">Image name</span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="imageStatus">
+        <th mat-header-cell *matHeaderCellDef class="status-col header-cell">
+          <span class="label image-label"> Status </span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="creationDate">
+        <th mat-header-cell *matHeaderCellDef class="shape-col header-cell">
+          <span class="label image-label"> Creation date </span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="endpoint">
+        <th mat-header-cell *matHeaderCellDef class="tag-col header-cell">
+          <span class="label image-label"> Endpoint </span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="templateName">
+        <th mat-header-cell *matHeaderCellDef class="resources-col 
label-header">
+          <span class="label image-label"> Template name </span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="sharedStatus">
+        <th mat-header-cell *matHeaderCellDef class="cost-col label-header">
+          <span class="label image-label"> Sharing </span>
+        </th>
+      </ng-container>
+
+      <ng-container matColumnDef="actions">
+        <th mat-header-cell *matHeaderCellDef class="settings label-header">
+          <span class="label image-label"> Actions </span>
+        </th>
+      </ng-container>
+
+      <!-- ----------------------------------------------------- -->
+
+      <ng-container matColumnDef="expandedDetail">
+        <td mat-cell *matCellDef="let element" class="exploratory" 
[attr.colspan]="8">
+          <tr *ngFor="let element of element.images; let i = index" 
class="element-row mat-row">
+            <td mat-cell class="image-checkbox--wrapper">
+              <datalab-checkbox
+                (click)="onCheckboxClick(element)"
+                class="image-checkbox"
+                [checked]="element.isSelected"
+              ></datalab-checkbox>
+            </td>
+
+            <td class="name-col image-table-cell">
+              <span>
+                {{ element.name }}
+              </span>
+            </td>
+            <td class="status-col status image-table-cell" ngClass="{{ 
element.status.toLowerCase() || ''}}">
+              {{element.status | titlecase}}
+            </td>
+            <td class="shape-col image-table-cell">
+              <span> {{element.timestamp | localDate : 'short'}} </span>
+            </td>
+
+            <td class="tag-col selection image-table-cell">
+              {{element.endpoint }}
+            </td>
+
+            <td class="resources-col image-table-cell">
+              {{element.templateName }}
+            </td>
+            <td class="cost-col image-table-cell">
+              <div class="shared-status--wrapper">
+                <span class="shared-status"> {{element.shared ? 
sharedStatus.shared : sharedStatus.private}} </span>
+                <span *ngIf="element.shared" class="currency_details" >
                 <i class="material-icons">help_outline</i>
               </span>
-        </div>
-      </td>
-    </ng-container>
+              </div>
+            </td>
 
-    <ng-container matColumnDef="actions">
-      <th mat-header-cell *matHeaderCellDef> {{tableHeaderCellTitles.actions}} 
</th>
-      <td mat-cell *matCellDef="let element" class="settings actions-col">
+            <td mat-cell class="settings actions-col">
+              <div class="button--wrapper action__button--wrapper">
+                <span class="currency_details" 
(click)="onImageInfoClick(element)">
+                  <i class="material-icons">help_outline</i>
+                </span>
+                <span #settings class="actions" 
(click)="actions.toggle($event, settings)"></span>
+              </div>
+              <bubble-up #actions class="list-menu" position="bottom-left" 
alternative="top-left">
+                <ul class="list-unstyled">
+                  <li [matTooltip]="element.status !== imageStatus.active && 
tooltipStatuses.activeOnly
+                      || userName !== element.user && 
tooltipStatuses.creatorOnly"
+                      matTooltipPosition="above"
+                      [matTooltipDisabled]="userName === element.user && 
element.status === imageStatus.active"
+                  >
+                    <button
+                      class="action-button__share"
+                      (click)="onShareClick(element)"
+                      [disabled]="userName !== element.user || element.status 
!== imageStatus.active"
+                    >
+                      <i class="material-icons">screen_share</i>
+                      <span>Share</span>
+                    </button>
+                  </li>
+                  <li
+                    [matTooltip]=tooltipStatuses.unableTerminate
+                    matTooltipPosition="above"
+                  >
+                    <button class="action-button__share">
+                      <i class="material-icons">phonelink_off</i>
+                      <span>Terminate</span>
+                    </button>
+                  </li>
+                </ul>
+              </bubble-up>
+            </td>
+          </tr>
+        </td>
+      </ng-container>
 
-        <div class="button--wrapper">
-          <span class="currency_details" (click)="onImageInfoClick(element)">
-            <i class="material-icons">help_outline</i>
-          </span>
-          <span #settings class="actions" (click)="actions.toggle($event, 
settings)"></span>
-        </div>
-        <bubble-up #actions class="list-menu" position="bottom-left" 
alternative="top-left">
-          <ul class="list-unstyled">
-            <li [matTooltip]="element.status !== imageStatus.active && 
tooltipStatuses.activeOnly
-                  || userName !== element.user && tooltipStatuses.creatorOnly"
-                matTooltipPosition="above"
-                [matTooltipDisabled]="userName === element.user && 
element.status === imageStatus.active"
-            >
-              <button
-                class="action-button__share"
-                (click)="onShareClick(element)"
-                [disabled]="userName !== element.user || element.status !== 
imageStatus.active"
-              >
-                <i class="material-icons">screen_share</i>
-                <span>Share</span>
-              </button>
-            </li>
-            <li
-              [matTooltip]=tooltipStatuses.unableTerminate
-              matTooltipPosition="above"
-            >
-              <button class="action-button__share">
-                <i class="material-icons">phonelink_off</i>
-                <span>Terminate</span>
-              </button>
-            </li>
-          </ul>
-        </bubble-up>
-      </td>
-    </ng-container>
-
-    <ng-container matColumnDef="placeholder">
-      <td mat-footer-cell *matFooterCellDef class="info" 
[colSpan]="displayedColumns.length - 1">
-        <span> There are no images yet </span>
-      </td>
-    </ng-container>
-
-    <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
-    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
-    <tr [hidden]="(dataSource | async)?.length" mat-footer-row 
*matFooterRowDef="['placeholder']"></tr>
-  </table>
+      <ng-container matColumnDef="placeholder">
+        <td mat-footer-cell *matFooterCellDef class="info" 
[colSpan]="displayedColumns.length - 1">
+          <span> There are no images yet </span>
+        </td>
+      </ng-container>
+
+      <tr mat-header-row *matHeaderRowDef="displayedColumns;" 
class="header-row"></tr>
+
+      <tr mat-row *matRowDef="let element; columns: ['project']" 
class="element-row"></tr>
+      <tr mat-row *matRowDef="let row; columns: ['expandedDetail']" 
class="detail-row"></tr>
+      <tr [hidden]="(dataSource | async)?.length" mat-footer-row 
*matFooterRowDef="['placeholder']"></tr>
+    </table>
 </section>
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
 
b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
index 680be511b..b7dae4ed1 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.scss
@@ -121,11 +121,12 @@
 .filer__wrapper {
   position: absolute;
   top: 20px;
-  left: 0px;
+  left: 170px;
   z-index: 10;
 }
 
 .filter__btn {
+  position: relative;
   margin-right: 10px;
   vertical-align: middle;
   width: 110px;
@@ -134,3 +135,111 @@
     line-height: 0;
   }
 }
+
+.show-active__btn {
+  line-height: 1;
+}
+
+.close__btn {
+  position: absolute;
+  top: 0px;
+  right: 2px;
+  width: 12px;
+  color: rgb(87, 114, 137);
+  background-color: transparent;
+  font-size: 15px;
+  border: none;
+  outline: none;
+
+  &:hover {
+    color: #35afd5;
+    cursor: pointer;
+  }
+}
+
+.settings {
+  text-align: end;
+}
+
+.image-page__project {
+  font-weight: 600;
+  color: #577289;
+  padding-left: 21px;
+}
+
+.image-checkbox--wrapper {
+    width: 4%;
+}
+
+table.resources .header-row > .header-cell {
+  padding: 5px 0;
+}
+
+table.resources .exploratory .element-row td.actions-col {
+  padding: 0;
+}
+
+table.resources .exploratory .element-row td,
+table.resources .header-row > .header-cell,
+table.resources .header-row th.label-header,{
+  padding: 0;
+}
+
+table.resources .header-row .label.image-label {
+  padding-top: 0px;
+  vertical-align: inherit !important;
+}
+
+.image-table-cell {
+  padding: 0 !important;
+}
+
+table.resources {
+  & .name-col.header-cell {
+    width: 15.5%;
+  }
+
+  & .shape-col.image-table-cell,
+  & .tag-col.image-table-cell {
+    width: 17%;
+  }
+
+  & .shape-col.header-cell,
+  & .tag-col.header-cell{
+    width: 15.7%;
+  }
+
+  & .resources-col.image-table-cell {
+    width: 23%;
+  }
+
+  & .resources-col.label-header {
+    width: 21.6%;
+  }
+
+  & .settings.actions-col,
+  & .settings.label-header {
+    width: 7%;
+  }
+
+  &  .status-col.header-cell {
+    width: 10.1%;
+  }
+
+  & .cost-col.label-header {
+    width: 12.5%;
+  }
+}
+
+.settings.image-table-cell,
+.settings.label-header {
+  width: 12%;
+}
+
+.action__button--wrapper {
+  width: 100%;
+}
+
+table.resources .label-header.settings {
+  padding-right: 15px !important;
+}
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 64d02cf60..139661a44 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
@@ -17,16 +17,16 @@
  * under the License.
  */
 
-import {Component, OnDestroy, OnInit} from '@angular/core';
+import { Component, OnDestroy, OnInit } from '@angular/core';
 import { MatDialog } from '@angular/material/dialog';
 import { Observable } from 'rxjs';
-import {map, tap} from 'rxjs/operators';
+import { map, tap} from 'rxjs/operators';
 
 import { ToastrService } from 'ngx-toastr';
 
 import { GeneralEnvironmentStatus } from 
'../../administration/management/management.model';
 import { HealthStatusService } from '../../core/services';
-import {FilterDropdownValue, ImageModel, ProjectModel} from './images.model';
+import { ImageFilterFormDropdownData, ImageFilterFormValue, ImageModel, 
ProjectModel } from './images.model';
 import {
   TooltipStatuses,
   Image_Table_Column_Headers,
@@ -34,13 +34,13 @@ import {
   ImageStatuses,
   Localstorage_Key,
   Placeholders,
-  Shared_Status, DropdownFieldNames,
+  Shared_Status, DropdownFieldNames, FilterFormInitialValue, 
ImageModelKeysForFilter,
 } from './images.config';
 import { ShareImageDialogComponent } from 
'../exploratory/share-image/share-image-dialog.component';
 import { ImagesService } from './images.service';
 import { ProgressBarService } from '../../core/services/progress-bar.service';
 import { ImageDetailDialogComponent } from 
'../exploratory/image-detail-dialog/image-detail-dialog.component';
-import {ActivatedRoute} from '@angular/router';
+import { ActivatedRoute } from '@angular/router';
 
 @Component({
   selector: 'datalab-images',
@@ -64,13 +64,17 @@ export class ImagesComponent implements OnInit, OnDestroy {
   isActionsOpen: boolean = false;
   healthStatus: GeneralEnvironmentStatus;
   dataSource: Observable<ImageModel[]>;
+  projectSource: Observable<ProjectModel[]>;
   checkboxSelected: boolean = false;
   projectList: string[] = [];
   activeProjectName: string = '';
-  userName!: string;
+  userName: string;
   isProjectsMoreThanOne: boolean;
   isFilterOpened: Observable<boolean>;
-  $filterDropdownData: Observable<FilterDropdownValue>;
+  $filterDropdownData: Observable<ImageFilterFormDropdownData>;
+  $filterFormValue: Observable<ImageFilterFormValue>;
+  isShowActive: boolean = true;
+  isFiltered: boolean = false;
 
   constructor(
     private healthStatusService: HealthStatusService,
@@ -88,10 +92,16 @@ export class ImagesComponent implements OnInit, OnDestroy {
     this.initImageTable();
     this.initFilterBtn();
     this.getDropdownList();
+    this.getFilterFormValue();
   }
 
   ngOnDestroy(): void {
     this.imagesService.closeFilter();
+    this.imagesService.setFilterFormValue(FilterFormInitialValue);
+  }
+
+  public trackBy(index, item) {
+    return null;
   }
 
   onCheckboxClick(element: ImageModel): void {
@@ -116,7 +126,7 @@ export class ImagesComponent implements OnInit, OnDestroy {
   }
 
   onRefreshClick(): void {
-    this.getUserImagePageInfo();
+    this.imagesService.getImagePageInfo().subscribe();
     this.activeProjectName = '';
   }
 
@@ -143,8 +153,10 @@ export class ImagesComponent implements OnInit, OnDestroy {
     this.imagesService.openFilter();
   }
 
-  onFilterApplyClick(filterFormValue): void {
-    console.log(filterFormValue);
+  onFilterApplyClick(filterFormValue: ImageFilterFormValue): void {
+    this.imagesService.filterImagePageInfo(filterFormValue).subscribe();
+    this.imagesService.setFilterFormValue(filterFormValue);
+    this.isFiltered = true;
     this.imagesService.closeFilter();
   }
 
@@ -152,14 +164,21 @@ export class ImagesComponent implements OnInit, OnDestroy 
{
     this.imagesService.closeFilter();
   }
 
-  onControlChanges(controlName: keyof FilterDropdownValue, inputValue: 
string): void {
+  onControlChanges(controlName: keyof ImageFilterFormDropdownData, inputValue: 
string): void {
     this.imagesService.filterDropdownField(DropdownFieldNames.imageName, 
inputValue);
   }
 
-  // onImageNameChange1(inputValue: string): void {
-  //   this.imagesService.filterDropdownField(inputValue);
-  //   console.log(inputValue);
-  // }
+  toggleShowActive(): void {
+    this.isShowActive = !this.isShowActive;
+    this.imagesService.showImage(this.isShowActive, 
ImageModelKeysForFilter.status, ImageStatuses.active);
+  }
+
+  onResetFilterClick(event: Event): void {
+    event.stopPropagation();
+    this.imagesService.filterImagePageInfo(FilterFormInitialValue).subscribe();
+    this.imagesService.setFilterFormValue(FilterFormInitialValue);
+    this.isFiltered = false;
+  }
 
   private getEnvironmentHealthStatus(): void {
     this.healthStatusService.getEnvironmentHealthStatus().subscribe(
@@ -173,12 +192,15 @@ export class ImagesComponent implements OnInit, OnDestroy 
{
   private getUserImagePageInfo(): void {
     this.route.data.pipe(
       map(data => data['projectList']),
-      tap(projectList => this.getProjectList(projectList))
+      tap(projectList => {
+        return this.getProjectList(projectList);
+      })
     ).subscribe();
   }
 
   private initImageTable(): void {
     this.dataSource = this.imagesService.$imageList;
+    this.projectSource = this.imagesService.$projectList;
   }
 
   private getProjectList(imagePageList: ProjectModel[]): void {
@@ -187,7 +209,7 @@ export class ImagesComponent implements OnInit, OnDestroy {
     }
     this.projectList = this.imagesService.getProjectNameList(imagePageList);
     this.isProjectsMoreThanOne = this.projectList.length > 1;
-    if (this.isProjectsMoreThanOne) {
+    if (!this.isProjectsMoreThanOne) {
       this.activeProjectName = this.projectList[0];
     }
   }
@@ -204,6 +226,10 @@ export class ImagesComponent implements OnInit, OnDestroy {
     this.$filterDropdownData = this.imagesService.$filterDropdownData;
   }
 
+  getFilterFormValue(): void {
+    this.$filterFormValue = this.imagesService.$filterFormValue;
+  }
+
   get isImageSelected(): boolean {
     return this.imagesService.isImageSelected();
   }
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 3db199434..4be84ca19 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
@@ -70,10 +70,9 @@ export enum TooltipStatuses {
 
 export enum DropdownFieldNames {
   imageName = 'imageName',
-  imageStatuses = 'imageStatuses',
   endpoints = 'endpoints',
   templateNames = 'templateNames',
-  sharingStatuses = 'sharingStatuses'
+  statuses = 'statuses'
 }
 
 export enum ImageModelNames {
@@ -83,3 +82,25 @@ export enum ImageModelNames {
   templateName = 'templateName',
   shared = 'shared'
 }
+
+export const FilterFormInitialValue = {
+    endpoints: [],
+    imageName: '',
+    statuses: [],
+    templateNames: [],
+};
+
+export const ChangedColumnStartValue = {
+    endpoints: false,
+    imageName: false,
+    statuses: false,
+    templateNames: false,
+};
+
+export enum ImageModelKeysForFilter {
+  status = 'status',
+  name = 'name',
+  endpoint = 'endpoint',
+  templateName = 'templateName',
+  shared = 'shared'
+}
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
 
b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
index f1fe519c9..620a9c5ee 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
@@ -49,10 +49,30 @@ export interface ClusterConfig {
   Configurations: any[];
 }
 
-export interface FilterDropdownValue {
+export interface ImageFilterFormDropdownData {
   imageName: string[];
   imageStatuses: string[];
   endpoints: string[];
   templateNames: string[];
   sharingStatuses: string[];
 }
+
+export interface ImageFilterFormValue {
+  endpoints: string[];
+  imageName: string;
+  statuses: string[];
+  templateNames: string[];
+}
+
+
+export interface LibraryInfoItem {
+  name: string;
+  libs: string[];
+}
+
+export interface FilteredColumnList {
+  imageName: boolean;
+  statuses: boolean;
+  endpoints: boolean;
+  templateNames: boolean;
+}
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 054e45bf7..2e62e0350 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
@@ -2,9 +2,17 @@ import { Injectable } from '@angular/core';
 import { tap } from 'rxjs/operators';
 import { BehaviorSubject, Observable } from 'rxjs';
 
-import { FilterDropdownValue, ImageModel, ProjectModel, 
ShareImageAllUsersParams } from './images.model';
+import {
+  FilteredColumnList,
+  ImageFilterFormDropdownData,
+  ImageFilterFormValue,
+  ImageModel,
+  ProjectModel,
+  ShareImageAllUsersParams
+} from './images.model';
 import { ApplicationServiceFacade, UserImagesPageService } from 
'../../core/services';
-import {ImageModelNames} from './images.config';
+import { ChangedColumnStartValue, FilterFormInitialValue, ImageModelNames } 
from './images.config';
+import { caseInsensitiveSortUtil } from '../../core/util';
 
 @Injectable({
   providedIn: 'root'
@@ -13,25 +21,40 @@ export class ImagesService {
   private $$projectList: BehaviorSubject<ProjectModel[]> = new 
BehaviorSubject<ProjectModel[]>([]);
   private $$imageList: BehaviorSubject<ImageModel[]> = new 
BehaviorSubject<ImageModel[]>([]);
   private $$isFilterOpened: BehaviorSubject<boolean> = new 
BehaviorSubject<boolean>(false);
-  private $$filterDropdownData: BehaviorSubject<FilterDropdownValue> = new 
BehaviorSubject<FilterDropdownValue>({} as FilterDropdownValue);
-  private dropdownStartValue: FilterDropdownValue;
+  // tslint:disable-next-line:max-line-length
+  private $$filterDropdownData: BehaviorSubject<ImageFilterFormDropdownData> = 
new BehaviorSubject<ImageFilterFormDropdownData>({} as 
ImageFilterFormDropdownData);
+  private $$filterFormValue: BehaviorSubject<ImageFilterFormValue> = new 
BehaviorSubject<ImageFilterFormValue>(FilterFormInitialValue);
+  private $$changedColumn: BehaviorSubject<FilteredColumnList> = new 
BehaviorSubject<FilteredColumnList>(ChangedColumnStartValue);
+  private dropdownStartValue: ImageFilterFormDropdownData;
 
+  $projectList = this.$$projectList.asObservable();
   $imageList = this.$$imageList.asObservable();
   $isFilterOpened = this.$$isFilterOpened.asObservable();
   $filterDropdownData = this.$$filterDropdownData.asObservable();
+  $filterFormValue = this.$$filterFormValue.asObservable();
 
   constructor(
     private applicationServiceFacade: ApplicationServiceFacade,
     private userImagesPageService: UserImagesPageService
   ) { }
 
-  getUserImagePageInfo(): Observable<ProjectModel[]> {
-    return this.userImagesPageService.getUserImagePageInfo()
+  getImagePageInfo(): Observable<ProjectModel[]> {
+    return this.userImagesPageService.getFilterImagePage()
       .pipe(
         tap(value => this.getImagePageData(value))
       );
   }
 
+  filterImagePageInfo(params: ImageFilterFormValue): 
Observable<ProjectModel[]> {
+    return this.userImagesPageService.filterImagePage(params)
+      .pipe(
+        tap(value => {
+          console.log(value);
+          return  this.getImagePageData(value);
+        })
+      );
+  }
+
   shareImageAllUsers(image: ImageModel): Observable<ProjectModel[]> {
     const shareParams: ShareImageAllUsersParams = {
       imageName: image.name,
@@ -82,11 +105,29 @@ export class ImagesService {
     this.$$isFilterOpened.next(false);
   }
 
-  filterDropdownField(field: keyof FilterDropdownValue, value: string, ) {
+  filterDropdownField(field: keyof ImageFilterFormDropdownData, value: string, 
) {
     const filteredDropdownList = this.dropdownStartValue[field].filter(item => 
item.toLowerCase().includes(value));
     this.addFilterDropdownData({...this.$$filterDropdownData.value, imageName: 
filteredDropdownList});
   }
 
+  setFilterFormValue(value: ImageFilterFormValue): void {
+    this.$$filterFormValue.next(value);
+  }
+
+  showImage(flag: boolean, field: keyof ImageModel, comparedValue: string) {
+    const imageList = this.getImageList(this.$$projectList.getValue());
+    if (flag) {
+      this.updateImageList(imageList);
+    } else {
+      const filteredImageList = this.filterByCondition(imageList, field, 
comparedValue);
+      this.updateImageList(filteredImageList);
+    }
+  }
+
+  private filterByCondition(arr: ImageModel[], field: keyof ImageModel, 
comparedValue: string) {
+    return  arr.filter(item => item[field] === comparedValue);
+  }
+
   private getDropdownDataList(): void {
     const dropdownList = {
       imageName: this.getDropdownDataItem(ImageModelNames.name),
@@ -95,7 +136,6 @@ export class ImagesService {
       templateNames: this.getDropdownDataItem(ImageModelNames.templateName),
       sharingStatuses: this.getDropdownDataItem(ImageModelNames.shared),
     };
-
     this.addFilterDropdownData(dropdownList);
     this.dropdownStartValue = dropdownList;
   }
@@ -105,17 +145,21 @@ export class ImagesService {
       acc.add(item[key].toString());
       return acc;
     }, new Set<string>());
-    return [...dropdownItem];
+    return caseInsensitiveSortUtil([...dropdownItem]);
   }
 
-  private addFilterDropdownData(data: FilterDropdownValue): void {
+  private addFilterDropdownData(data: ImageFilterFormDropdownData): void {
     this.$$filterDropdownData.next(data);
   }
 
   private getImagePageData(imagePageData: ProjectModel[]): void {
-    const imageList = imagePageData.reduce((acc: ImageModel[], {images}) => 
[...acc, ...images], []);
+    const imageList = this.getImageList(imagePageData);
     this.updateProjectList(imagePageData);
     this.updateImageList(imageList);
     this.getDropdownDataList();
   }
+
+  private getImageList(imagePageData: ProjectModel[]): ImageModel[] {
+    return imagePageData.reduce((acc: ImageModel[], {images}) => [...acc, 
...images], []);
+  }
 }
diff --git 
a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
 
b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
index 4677f57d2..0fa17510e 100644
--- 
a/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
+++ 
b/services/self-service/src/main/resources/webapp/src/app/resources/resources-grid/resources-grid.component.ts
@@ -444,6 +444,7 @@ export class ResourcesGridComponent implements OnInit {
         this.toastr.error('Creating computation resource failed!', 'Oops!');
       }
     }
+    console.log(filteredData);
     this.filteredEnvironments = filteredData;
   }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to