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/08/01 11:38:16 UTC
[incubator-datalab] 04/04: filter list reload implemented
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 <bo...@gmail.com>
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">×</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: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org