From 195ff79d49cfbf23d2cd827f5f9ec797a7f8ccc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20AUDEON?= <mael.audeon@teamdlab.com> Date: Tue, 28 Jul 2020 17:02:28 +0200 Subject: [PATCH] [TRTL-206] Add filters on status & level --- .../src/app/core/api/group-api.service.ts | 10 ++- .../group-list/group-criteria-builder.util.ts | 74 +++++++++++++++++++ .../group-list/group-list.component.html | 40 ++++++++++ .../group/group-list/group-list.component.ts | 39 +++++++++- .../identity/src/app/group/group.service.ts | 9 ++- 5 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 ui/ui-frontend/projects/identity/src/app/group/group-list/group-criteria-builder.util.ts diff --git a/ui/ui-frontend/projects/identity/src/app/core/api/group-api.service.ts b/ui/ui-frontend/projects/identity/src/app/core/api/group-api.service.ts index 6d7019cd..b686c941 100644 --- a/ui/ui-frontend/projects/identity/src/app/core/api/group-api.service.ts +++ b/ui/ui-frontend/projects/identity/src/app/core/api/group-api.service.ts @@ -38,7 +38,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { BASE_URL, BaseHttpClient, Group, PageRequest, PaginatedResponse } from 'ui-frontend-common'; +import {BASE_URL, BaseHttpClient, Group, PageRequest, PaginatedResponse, SearchQuery} from 'ui-frontend-common'; @Injectable({ providedIn: 'root' @@ -77,4 +77,12 @@ export class GroupApiService extends BaseHttpClient<Group> { return super.patch(groupPartial, headers); } + getLevels(query?: SearchQuery, headers?: HttpHeaders): Observable<string[]> { + let params = new HttpParams(); + if (query) { + params = params.set('criteria', JSON.stringify(query)); + } + + return this.http.get<string[]>(this.apiUrl + '/levels', { params, headers }); + } } diff --git a/ui/ui-frontend/projects/identity/src/app/group/group-list/group-criteria-builder.util.ts b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-criteria-builder.util.ts new file mode 100644 index 00000000..62a7f480 --- /dev/null +++ b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-criteria-builder.util.ts @@ -0,0 +1,74 @@ +/* + * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020) + * and the signatories of the "VITAM - Accord du Contributeur" agreement. + * + * contact@programmevitam.fr + * + * This software is a computer program whose purpose is to implement + * implement a digital archiving front-office system for the secure and + * efficient high volumetry VITAM solution. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL-C license and that you accept its terms. + */ +import { buildCriteriaFromFilters, Criterion, Operators, SearchQuery } from 'ui-frontend-common'; + +const GROUP_FILTER_CONVERTER: Readonly<{ [key: string]: (values: any[]) => Array<Criterion | SearchQuery> }> = { + + status: (statusList: string[]): Criterion[] => { + if (statusList.length === 0) { + return []; + } + + const mappedList: boolean[] = []; + statusList.forEach((status) => { + switch (status) { + case 'ENABLED': + mappedList.push(true); + break; + case 'DISABLED': + mappedList.push(false); + break; + } + }); + return [{ key: 'enabled', value: mappedList, operator: Operators.in }]; + }, + level: (levelList: string[]): Criterion[] => { + if (levelList.length === 0) { + return []; + } + + if (levelList.some((level) => level === null)) { + levelList.push(''); + } + + return [{ key: 'level', value: levelList, operator: Operators.in }]; + } +}; + +export function buildCriteriaFromGroupFilters(filterMap: { [key: string]: any[] }) { + return buildCriteriaFromFilters(filterMap, GROUP_FILTER_CONVERTER); +} diff --git a/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.html b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.html index 33e84304..3e6e799d 100644 --- a/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.html +++ b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.html @@ -7,6 +7,30 @@ <tr> <th> <div class="vitamui-table-header"> + <button + class="vitamui-filter-button" + [vitamuiCommonTableFilter]="statusFilterTemplate" + > + <i class="material-icons vitamui-row-icon">filter_list</i> + </button> + + <ng-template #statusFilterTemplate> + <vitamui-common-table-filter [(filter)]="this.filterMap['status']" (filterChange)="onFilterChange('status', $event)"> + <vitamui-common-table-filter-option value="ENABLED"> + <div class="table-filter-icon" i18n="@@groupStatusEnabled"> + <i class="vitamui-icon vitamui-icon-user status-badge status-badge-green close"></i> + <span class="badge-state">Actif</span> + </div> + </vitamui-common-table-filter-option> + <vitamui-common-table-filter-option value="DISABLED"> + <div class="table-filter-icon" i18n="@@groupStatusDisabled"> + <i class="vitamui-icon vitamui-icon-user status-badge status-badge-grey close"></i> + <span class="badge-state">Désactivé</span> + </div> + </vitamui-common-table-filter-option> + </vitamui-common-table-filter> + </ng-template> + <i class="vitamui-icon vitamui-icon-keys vitamui-row-icon"></i> <vitamui-common-order-by-button orderByKey="status" [(orderBy)]="orderBy" [(direction)]="direction" (orderChange)="emitOrderChange()"></vitamui-common-order-by-button> @@ -35,6 +59,22 @@ </th> <th> <div class="vitamui-table-header"> + <button class="vitamui-filter-button" [vitamuiCommonTableFilter]="levelFilterTemplate" + [class.active]="filterMap['level'] && filterMap['level'].length > 0" #levelFilterTrigger="vitamuiCommonTableFilter"> + <i class="material-icons vitamui-row-icon">filter_list</i> + </button> + + <ng-template #levelFilterTemplate> + <vitamui-common-table-filter-search + [(filter)]="filterMap['level']" + [options]="levelFilterOptions" + (filterChange)="onFilterChange('level', $event)" + (filterClose)="levelFilterTrigger?.close()" + emptyValueOption="-Niveau vide-" + i18n-emptyValueOption="@@groupsListLevelFilterEmpty" + ></vitamui-common-table-filter-search> + </ng-template> + <span i18n="Description@@profileGroupListHeaderLevel">Niveau</span> <vitamui-common-order-by-button orderByKey="level" [(orderBy)]="orderBy" [(direction)]="direction" (orderChange)="emitOrderChange()"></vitamui-common-order-by-button> diff --git a/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.ts b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.ts index 404b6733..dad34589 100644 --- a/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.ts +++ b/ui/ui-frontend/projects/identity/src/app/group/group-list/group-list.component.ts @@ -35,7 +35,7 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { animate, state, style, transition, trigger } from '@angular/animations'; -import {Component, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Inject, Input, LOCALE_ID, OnDestroy, OnInit, Output} from '@angular/core'; import {merge, Subject, Subscription} from 'rxjs'; import { @@ -47,6 +47,7 @@ import { SearchQuery } from 'ui-frontend-common'; import { GroupService } from '../group.service'; +import {buildCriteriaFromGroupFilters} from './group-criteria-builder.util'; @Component({ selector: 'app-group-list', @@ -85,18 +86,29 @@ export class GroupListComponent extends InfiniteScrollTable<Group> implements On 'description' ]; + filterMap: { [key: string]: any[] } = { + status: ['ENABLED'], + level: null + }; + orderBy = 'name'; direction = Direction.ASCENDANT; + private readonly filterChange = new Subject<{ [key: string]: any[] }>(); private readonly orderChange = new Subject<string>(); private readonly searchChange = new Subject<string>(); - constructor(public groupService: GroupService) { + levelFilterOptions: Array<{ value: string, label: string }> = []; + + constructor(public groupService: GroupService, + @Inject(LOCALE_ID) private locale: string) { super(groupService); } ngOnInit() { this.search(); + this.refreshLevelOptions(); + this.updatedGroupSub = this.groupService.updated.subscribe((updatedGroup: Group) => { const profileGroupIndex = this.dataSource.findIndex((group) => updatedGroup.id === group.id); if (profileGroupIndex > -1) { @@ -105,11 +117,14 @@ export class GroupListComponent extends InfiniteScrollTable<Group> implements On }); - const searchCriteriaChange = merge(this.searchChange, this.orderChange); + const searchCriteriaChange = merge(this.searchChange, this.filterChange, this.orderChange); searchCriteriaChange.subscribe(() => { const query: SearchQuery = { - criteria: buildCriteriaFromSearch(this._search, this.searchKeys) + criteria: [ + ...buildCriteriaFromGroupFilters(this.filterMap), + ...buildCriteriaFromSearch(this._search, this.searchKeys) + ] }; const pageRequest = new PageRequest(0, DEFAULT_PAGE_SIZE, this.orderBy, this.direction, JSON.stringify(query)); @@ -118,6 +133,11 @@ export class GroupListComponent extends InfiniteScrollTable<Group> implements On } + onFilterChange(key: string, values: any[]) { + this.filterMap[key] = values; + this.filterChange.next(this.filterMap); + } + emitOrderChange() { this.orderChange.next(); } @@ -126,4 +146,15 @@ export class GroupListComponent extends InfiniteScrollTable<Group> implements On this.updatedGroupSub.unsubscribe(); } + private refreshLevelOptions(query?: SearchQuery) { + this.groupService.getNonEmptyLevels(query).subscribe((levels: string[]) => { + this.levelFilterOptions = levels.map((level: string) => ({value: level, label: level })); + this.levelFilterOptions.sort(sortByLabel(this.locale)); + }); + } +} + + +function sortByLabel(locale: string): (a: { label: string }, b: { label: string }) => number { + return (a: { label: string }, b: { label: string }) => a.label.localeCompare(b.label, locale); } diff --git a/ui/ui-frontend/projects/identity/src/app/group/group.service.ts b/ui/ui-frontend/projects/identity/src/app/group/group.service.ts index 3d4bbd92..e91f87dd 100644 --- a/ui/ui-frontend/projects/identity/src/app/group/group.service.ts +++ b/ui/ui-frontend/projects/identity/src/app/group/group.service.ts @@ -35,7 +35,7 @@ * knowledge of the CeCILL-C license and that you accept its terms. */ import { Observable, Subject } from 'rxjs'; -import { tap } from 'rxjs/operators'; +import {map, tap} from 'rxjs/operators'; import { Criterion, Group, Operators, SearchQuery, SearchService } from 'ui-frontend-common'; import { HttpClient, HttpParams } from '@angular/common/http'; @@ -133,4 +133,11 @@ export class GroupService extends SearchService<Group> { return this.groupApi.getAllByParams(params); } + + getNonEmptyLevels(query: SearchQuery) { + return this.groupApi.getLevels(query) + .pipe( + map((levels) => levels.filter((l) => !!l)) + ); + } } -- GitLab