From b95410f6fdf0d98e3c4a9d9e0cc0c207dcf7cead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Fri, 22 Oct 2021 13:39:12 +0300 Subject: [PATCH] RED-2544: User management filters --- .../users-stats/users-stats.component.html | 1 + .../user-listing-screen.component.ts | 58 +++++++++---------- .../simple-doughnut-chart.component.ts | 25 ++++---- apps/red-ui/src/app/utils/filter-utils.ts | 16 ++++- libs/red-domain/src/lib/users/user.model.ts | 2 +- 5 files changed, 58 insertions(+), 44 deletions(-) diff --git a/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.html b/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.html index 0788af428..9267a01e9 100644 --- a/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.html +++ b/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.html @@ -25,6 +25,7 @@ [strokeWidth]="15" [subtitle]="'user-stats.chart.users' | translate" direction="row" + filterKey="roleFilters" totalType="sum" > diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts index f0648879b..35f529d98 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts @@ -11,6 +11,7 @@ import { IconButtonTypes, ListingComponent, LoadingService, + NestedFilter, TableColumnConfig, } from '@iqser/common-ui'; import { Observable } from 'rxjs'; @@ -18,6 +19,7 @@ import { map } from 'rxjs/operators'; import { rolesTranslations } from '../../../../translations/roles-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { User } from '@red/domain'; +import { userTypeChecker, userTypeFilters, UserTypes } from '../../../../utils'; @Component({ templateUrl: './user-listing-screen.component.html', @@ -110,38 +112,30 @@ export class UserListingScreenComponent extends ListingComponent implement private _computeStats() { this.chartData = this._translateChartService.translateRoles( - [ - { - value: this.allEntities.filter(user => !user.isActive).length, - color: 'INACTIVE', - label: 'INACTIVE', - }, - { - value: this.allEntities.filter(user => user.roles.length === 1 && user.roles[0] === 'RED_USER').length, - color: 'REGULAR', - label: 'REGULAR', - }, - { - value: this.allEntities.filter(user => user.isManager && !user.isAdmin).length, - color: 'MANAGER', - label: 'RED_MANAGER', - }, - { - value: this.allEntities.filter(user => user.isManager && user.isAdmin).length, - color: 'MANAGER_ADMIN', - label: 'MANAGER_ADMIN', - }, - { - value: this.allEntities.filter(user => user.isUserAdmin && !user.isAdmin).length, - color: 'USER_ADMIN', - label: 'RED_USER_ADMIN', - }, - { - value: this.allEntities.filter(user => user.isAdmin && !user.isManager).length, - color: 'ADMIN', - label: 'RED_ADMIN', - }, - ].filter(type => type.value > 0), + UserTypes.map(type => ({ + value: this.allEntities.filter(userTypeFilters[type]).length, + color: type.replace('RED_', ''), + label: type, + key: type, + })).filter(type => type.value > 0), ); + + this._computeAllFilters(); + } + + private _computeAllFilters() { + const roleFilters = this.chartData.map( + config => + new NestedFilter({ + id: config.key, + label: config.label, + }), + ); + + this.filterService.addFilterGroup({ + slug: 'roleFilters', + filters: roleFilters, + checker: userTypeChecker, + }); } } diff --git a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts index a95aea2f8..6524e07d2 100644 --- a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts @@ -1,6 +1,6 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { Color } from '@utils/types'; -import { FilterService } from '@iqser/common-ui'; +import { FilterService, INestedFilter } from '@iqser/common-ui'; import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,14 +12,12 @@ export interface DoughnutChartConfig { active?: boolean; } -const FILTER_KEY = 'statusFilters'; - @Component({ selector: 'redaction-simple-doughnut-chart', templateUrl: './simple-doughnut-chart.component.html', styleUrls: ['./simple-doughnut-chart.component.scss'], }) -export class SimpleDoughnutChartComponent implements OnChanges { +export class SimpleDoughnutChartComponent implements OnChanges, OnInit { @Input() subtitle: string; @Input() config: DoughnutChartConfig[] = []; @Input() radius = 85; @@ -27,7 +25,8 @@ export class SimpleDoughnutChartComponent implements OnChanges { @Input() direction: 'row' | 'column' = 'column'; @Input() totalType: 'sum' | 'count' = 'sum'; @Input() counterText: string; - readonly filtersEnabled: boolean; + @Input() filterKey = 'statusFilters'; + filtersEnabled: boolean; chartData: any[] = []; perimeter: number; @@ -35,10 +34,12 @@ export class SimpleDoughnutChartComponent implements OnChanges { cy = 0; size = 0; - readonly statusFilters$ = this.filterService.getFilterModels$(FILTER_KEY) ?? of([]); + filters$: Observable; constructor(readonly filterService: FilterService) { - this.filtersEnabled = !!this.filterService.filterGroups.find(g => g.slug === FILTER_KEY); + this.filterService.filterGroups$.subscribe(() => { + this.filtersEnabled = !!this.filterService.filterGroups.find(g => g.slug === this.filterKey); + }); } get circumference(): number { @@ -53,6 +54,10 @@ export class SimpleDoughnutChartComponent implements OnChanges { return this.totalType === 'sum' ? this.dataTotal : this.config.length; } + ngOnInit() { + this.filters$ = this.filterService.getFilterModels$(this.filterKey) ?? of([]); + } + ngOnChanges(): void { this.calculateChartData(); this.cx = this.radius + this.strokeWidth / 2; @@ -61,7 +66,7 @@ export class SimpleDoughnutChartComponent implements OnChanges { } filterChecked$(key: string): Observable { - return this.statusFilters$.pipe(map(all => all?.find(e => e.id === key)?.checked)); + return this.filters$.pipe(map(all => all?.find(e => e.id === key)?.checked)); } calculateChartData() { @@ -96,7 +101,7 @@ export class SimpleDoughnutChartComponent implements OnChanges { selectValue(key: string): void { if (this.filtersEnabled) { - this.filterService.toggleFilter(FILTER_KEY, key); + this.filterService.toggleFilter(this.filterKey, key); } } } diff --git a/apps/red-ui/src/app/utils/filter-utils.ts b/apps/red-ui/src/app/utils/filter-utils.ts index 9b00d390d..815465856 100644 --- a/apps/red-ui/src/app/utils/filter-utils.ts +++ b/apps/red-ui/src/app/utils/filter-utils.ts @@ -1,5 +1,5 @@ import { File } from '@models/file/file'; -import { Dossier } from '@red/domain'; +import { Dossier, User } from '@red/domain'; import { handleCheckedValue, INestedFilter } from '@iqser/common-ui'; export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) { @@ -85,3 +85,17 @@ export const dossierMemberChecker = (dw: Dossier, filter: INestedFilter) => dw.h export const dossierTemplateChecker = (dw: Dossier, filter: INestedFilter) => dw.dossierTemplateId === filter.id; export const dossierApproverChecker = (dw: Dossier, filter: INestedFilter) => dw.approverIds.includes(filter.id); + +export const UserTypes = ['INACTIVE', 'REGULAR', 'RED_MANAGER', 'MANAGER_ADMIN', 'RED_USER_ADMIN', 'RED_ADMIN'] as const; +export type UserType = typeof UserTypes[number]; + +export const userTypeFilters: { [key in UserType]: (user: User) => boolean } = { + INACTIVE: (user: User) => !user.isActive, + REGULAR: (user: User) => user.roles.length === 1 && user.roles[0] === 'RED_USER', + RED_MANAGER: (user: User) => user.isManager && !user.isAdmin, + MANAGER_ADMIN: (user: User) => user.isManager && user.isAdmin, + RED_USER_ADMIN: (user: User) => user.isUserAdmin && !user.isAdmin, + RED_ADMIN: (user: User) => user.isAdmin && !user.isManager, +}; + +export const userTypeChecker = (user: User, filter: INestedFilter) => userTypeFilters[filter.id](user); diff --git a/libs/red-domain/src/lib/users/user.model.ts b/libs/red-domain/src/lib/users/user.model.ts index 4d1ca7663..743cfe8ec 100644 --- a/libs/red-domain/src/lib/users/user.model.ts +++ b/libs/red-domain/src/lib/users/user.model.ts @@ -23,7 +23,7 @@ export class User implements IUser, IListable { this.firstName = user.firstName; this.lastName = user.lastName; this.name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username; - this.searchKey = `${this.name}${this.username}${this.email}`; + this.searchKey = `${this.name || '-'}${this.username || '-'}${this.email || ''}`; } hasRole(role: string): boolean {