Skip to content
Snippets Groups Projects
Commit 195ff79d authored by Maël AUDEON's avatar Maël AUDEON
Browse files

[TRTL-206] Add filters on status & level

parent caef0319
No related branches found
No related tags found
1 merge request!1Feature/design/1
......@@ -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 });
}
}
/*
* 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);
}
......@@ -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>
......
......@@ -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);
}
......@@ -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))
);
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment