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 d01c5f95744b8bd375d3a54eb465746512af08f1 Author: Hennadii_Shpak <[email protected]> AuthorDate: Thu Aug 4 00:10:36 2022 +0300 DATALAB-2881 is finished, implemented reset filter by column name --- .../src/app/resources/images/images.component.html | 72 ++++++++++++++++++---- .../src/app/resources/images/images.component.scss | 9 +++ .../src/app/resources/images/images.component.ts | 25 ++++++-- .../src/app/resources/images/images.config.ts | 5 +- .../src/app/resources/images/images.model.ts | 4 ++ .../src/app/resources/images/images.service.ts | 67 ++++++++++++++++---- 6 files changed, 153 insertions(+), 29 deletions(-) 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 15f1c2533..b782f49a3 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 @@ -94,10 +94,10 @@ </div> </span> - <button mat-raised-button [disabled]="!(dataSource | async)?.length && !isFiltered" class="butt filter__btn" (click)="onFilterClick()"> - <i class="material-icons" [ngClass]="{'filtered-icon': isFiltered}">filter_list</i> + <button mat-raised-button [disabled]="!(dataSource | async)?.length && !($isFiltered | async)" class="butt filter__btn" (click)="onFilterClick()"> + <i class="material-icons" [ngClass]="{'filtered-icon': $isFiltered | async}">filter_list</i> <span class="filter__btn--name">Filter</span> - <button *ngIf="isFiltered" type="button" (click)="onResetFilterClick($event)" class="close__btn">×</button> + <button *ngIf="$isFiltered | async" type="button" (click)="onResetFilterClick($event)" class="close__btn">×</button> </button> <div *ngIf="isFilterOpened | async" class="filer__wrapper"> <datalab-page-filter @@ -166,43 +166,93 @@ <ng-container matColumnDef="imageName"> <th mat-header-cell *matHeaderCellDef class="name-col header-cell"> - <span class="label image-label">Image name</span> + <span class="label image-label"> + {{tableHeaderCellTitles.imageName}} + <button + *ngIf="($filteredColumnState | async)?.imageName" + type="button" + class="close__btn header__close--btn" + (click)="onResetColumn(dropdownFieldNames.imageName)" + > + × + </button> + </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> + <span class="label image-label"> + {{tableHeaderCellTitles.imageStatus}} + <button + *ngIf="($filteredColumnState | async)?.statuses" + type="button" + class="close__btn header__close--btn" + (click)="onResetColumn(dropdownFieldNames.statuses)" + > + × + </button> + </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> + <span class="label image-label"> {{tableHeaderCellTitles.creationDate}} </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> + <span class="label image-label"> + {{tableHeaderCellTitles.endpoint}} + <button + *ngIf="($filteredColumnState | async)?.endpoints" + type="button" + class="close__btn header__close--btn" + (click)="onResetColumn(dropdownFieldNames.endpoints)" + > + × + </button> + </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> + <span class="label image-label"> + {{tableHeaderCellTitles.templateName}} + <button + *ngIf="($filteredColumnState | async)?.templateNames" + type="button" + class="close__btn header__close--btn" + (click)="onResetColumn(dropdownFieldNames.templateNames)" + > + × + </button> + </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> + <span class="label image-label"> + {{tableHeaderCellTitles.sharedStatus}} + <button + *ngIf="($filteredColumnState | async)?.sharingStatuses" + type="button" + class="close__btn header__close--btn" + (click)="onResetColumn(dropdownFieldNames.sharingStatuses)" + > + × + </button> + </span> </th> </ng-container> <ng-container matColumnDef="actions"> <th mat-header-cell *matHeaderCellDef class="settings label-header"> - <span class="label image-label"> Actions </span> + <span class="label image-label"> {{tableHeaderCellTitles.actions}} </span> </th> </ng-container> @@ -285,7 +335,7 @@ <ng-container matColumnDef="placeholder"> <td mat-footer-cell *matFooterCellDef class="info" [colSpan]="displayedColumns.length - 1"> - <span>{{ isFiltered ? 'No matches found' : 'There are no images yet' }}</span> + <span>{{ ($isFiltered | async) ? 'No matches found' : 'There are no images yet' }}</span> </td> </ng-container> 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 92be0c3a1..e317f2cde 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 @@ -252,3 +252,12 @@ td.mat-cell.image-page__project { .filtered-icon { color: #35afd5; } + +.label { + position: relative; +} + +.header__close--btn.close__btn { + top: -15px; + right: -15px; +} 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 f1066cd31..7d9a7ee12 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 @@ -26,7 +26,7 @@ import { ToastrService } from 'ngx-toastr'; import { GeneralEnvironmentStatus } from '../../administration/management/management.model'; import { HealthStatusService } from '../../core/services'; -import { ImageFilterFormDropdownData, ImageFilterFormValue, ImageModel, ProjectModel } from './images.model'; +import { FilteredColumnList, ImageFilterFormDropdownData, ImageFilterFormValue, ImageModel, ProjectModel } from './images.model'; import { TooltipStatuses, Image_Table_Column_Headers, @@ -78,8 +78,9 @@ export class ImagesComponent implements OnInit, OnDestroy { $filterDropdownData: Observable<ImageFilterFormDropdownData>; $filterFormValue: Observable<ImageFilterFormValue>; $isProjectListEmpty: Observable<boolean>; + $filteredColumnState: Observable<FilteredColumnList>; + $isFiltered: Observable<boolean>; isShowActive: boolean = true; - isFiltered: boolean = false; constructor( private healthStatusService: HealthStatusService, @@ -99,6 +100,8 @@ export class ImagesComponent implements OnInit, OnDestroy { this.getDropdownList(); this.getFilterFormValue(); this.getIsProjectListEmpty(); + this.initFilteredColumnState(); + this.initIsImageListFiltered(); } ngOnDestroy(): void { @@ -158,9 +161,10 @@ export class ImagesComponent implements OnInit, OnDestroy { onFilterApplyClick(filterFormValue: ImageFilterFormValue): void { const normalizeFilterFormValue = this.imagesService.normalizeFilterFormValue(filterFormValue, DropdownSelectAllValue); + this.imagesService.updateFilterColumnState(normalizeFilterFormValue); this.imagesService.filterImagePageInfo(normalizeFilterFormValue).subscribe(); this.imagesService.setFilterFormValue(filterFormValue); - this.isFiltered = true; + this.imagesService.checkIsPageFiltered(); this.imagesService.closeFilter(); } @@ -182,7 +186,12 @@ export class ImagesComponent implements OnInit, OnDestroy { event.stopPropagation(); this.imagesService.filterImagePageInfo(FilterFormInitialValue).subscribe(); this.imagesService.setFilterFormValue(FilterFormInitialValue); - this.isFiltered = false; + this.imagesService.updateFilterColumnState(FilterFormInitialValue); + this.imagesService.checkIsPageFiltered(); + } + + onResetColumn(dropdownFieldNames: DropdownFieldNames): void { + this.imagesService.resetFilterField(dropdownFieldNames, DropdownSelectAllValue); } private getEnvironmentHealthStatus(): void { @@ -194,6 +203,10 @@ export class ImagesComponent implements OnInit, OnDestroy { ); } + private initFilteredColumnState(): void { + this.$filteredColumnState = this.imagesService.$filteredColumnState; + } + private getUserImagePageInfo(): void { this.route.data.pipe( map(data => data['projectList']), @@ -239,6 +252,10 @@ export class ImagesComponent implements OnInit, OnDestroy { this.$isProjectListEmpty = this.imagesService.$isProjectListEmpty; } + private initIsImageListFiltered(): void { + this.$isFiltered = this.imagesService.$isImageListFiltered; + } + 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 0fa0c8232..06867d403 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 @@ -21,7 +21,7 @@ export enum Image_Table_Column_Headers { imageName = 'Image name', creationDate = 'Creation date', provider = 'Provider', - imageStatus = 'Image status', + imageStatus = 'Status', sharedStatus = 'Shared status', templateName = 'Template name', actions = 'Actions', @@ -90,6 +90,7 @@ export const FilterFormInitialValue = { imageName: '', statuses: [], templateNames: [], + sharingStatuses: [], }; export const ChangedColumnStartValue = { @@ -97,6 +98,7 @@ export const ChangedColumnStartValue = { imageName: false, statuses: false, templateNames: false, + sharingStatuses: false, }; export enum ImageModelKeysForFilter { @@ -108,3 +110,4 @@ export enum ImageModelKeysForFilter { } export const DropdownSelectAllValue = 'selectAllFound'; + 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 b3a66902f..e66b8db1a 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 @@ -62,6 +62,7 @@ export interface ImageFilterFormValue { imageName: string; statuses: string[]; templateNames: string[]; + sharingStatuses: string[]; } @@ -75,4 +76,7 @@ export interface FilteredColumnList { statuses: boolean; endpoints: boolean; templateNames: boolean; + sharingStatuses: boolean; } + +export type FilterFormItemType = [string, string[] | string]; 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 e8715ff8b..05dce5415 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 @@ -4,6 +4,7 @@ import { BehaviorSubject, Observable } from 'rxjs'; import { FilteredColumnList, + FilterFormItemType, ImageFilterFormDropdownData, ImageFilterFormValue, ImageModel, @@ -26,7 +27,8 @@ export class ImagesService { // 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 $$filteredColumnState: BehaviorSubject<FilteredColumnList> = new BehaviorSubject<FilteredColumnList>(ChangedColumnStartValue); + private $$isImageListFiltered: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private dropdownStartValue: ImageFilterFormDropdownData; $projectList = this.$$projectList.asObservable(); @@ -35,6 +37,8 @@ export class ImagesService { $isFilterOpened = this.$$isFilterOpened.asObservable(); $filterDropdownData = this.$$filterDropdownData.asObservable(); $filterFormValue = this.$$filterFormValue.asObservable(); + $filteredColumnState = this.$$filteredColumnState.asObservable(); + $isImageListFiltered = this.$$isImageListFiltered.asObservable(); constructor( private applicationServiceFacade: ApplicationServiceFacade, @@ -114,11 +118,21 @@ export class ImagesService { this.$$isFilterOpened.next(false); } - filterDropdownField(field: keyof ImageFilterFormDropdownData, value: string, ) { + filterDropdownField(field: keyof ImageFilterFormDropdownData, value: string, ): void { const filteredDropdownList = this.dropdownStartValue[field].filter(item => item.toLowerCase().includes(value)); this.addFilterDropdownData({...this.$$filterDropdownData.value, imageName: filteredDropdownList}); } + resetFilterField(field: keyof ImageFilterFormDropdownData, exceptionValue: string = ''): void { + const droppedFieldValue = this.getDroppedFieldValue(field); + const updatedFilterFormValue = {...this.$$filterFormValue.value, [field]: droppedFieldValue}; + const normalizeFormValue = this.normalizeFilterFormValue(updatedFilterFormValue, exceptionValue); + this.setFilterFormValue(updatedFilterFormValue); + this.updateFilterColumnState(normalizeFormValue); + this.filterImagePageInfo(normalizeFormValue).subscribe(); + this.checkIsPageFiltered(); + } + setFilterFormValue(value: ImageFilterFormValue): void { this.$$filterFormValue.next(value); } @@ -138,17 +152,44 @@ export class ImagesService { return filterFormValue; } return (<any>Object).entries(filterFormValue) - .reduce((acc, fieldItem) => { - const [ fieldName, fieldValue ] = fieldItem; - let value; - - if (typeof fieldValue === 'string') { - value = fieldValue; - } else { - value = fieldValue.filter(item => item !== exceptionValue); - } - return {...acc, [fieldName]: value}; - }, <ImageFilterFormValue>{}); + .reduce((acc, fieldItem) => this.filterFormValue(acc, fieldItem, exceptionValue), <ImageFilterFormValue>{}); + } + + updateFilterColumnState(filterFormValue: ImageFilterFormValue): void { + const columnStateList = (<any>Object).entries(filterFormValue) + .reduce((acc, fieldItem) => this.checkColumnState(acc, fieldItem), <FilteredColumnList>{}); + + this.$$filteredColumnState.next(columnStateList); + } + + getDroppedFieldValue(field: keyof ImageFilterFormDropdownData): string | [] { + return typeof this.$$filterFormValue.value[field] === 'string' + ? '' + : []; + } + + checkIsPageFiltered() { + const isImageListFiltered = (<any>Object).values(this.$$filteredColumnState.value).some(item => Boolean(item)); + this.$$isImageListFiltered.next(isImageListFiltered); + } + + private checkColumnState(acc: FilteredColumnList, fieldItem: FilterFormItemType): FilteredColumnList { + const [ fieldName, fieldValue ] = fieldItem; + let isColumnFiltered: boolean; + isColumnFiltered = typeof fieldValue === 'string' ? Boolean(fieldValue) : Boolean(fieldValue.length); + return {...acc, [fieldName]: isColumnFiltered}; + } + + private filterFormValue(acc: ImageFilterFormValue, fieldItem: FilterFormItemType, exceptionValue: string = ''): ImageFilterFormValue { + const [ fieldName, fieldValue ] = fieldItem; + let value; + + if (typeof fieldValue === 'string') { + value = fieldValue; + } else { + value = fieldValue.filter(item => item !== exceptionValue); + } + return {...acc, [fieldName]: value}; } private filterByCondition(arr: ProjectModel[], field: keyof ImageModel, comparedValue: string) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
