RED-2544: User management filters

This commit is contained in:
Adina Țeudan 2021-10-22 13:39:12 +03:00
parent fa3125302f
commit b95410f6fd
5 changed files with 58 additions and 44 deletions

View File

@ -25,6 +25,7 @@
[strokeWidth]="15" [strokeWidth]="15"
[subtitle]="'user-stats.chart.users' | translate" [subtitle]="'user-stats.chart.users' | translate"
direction="row" direction="row"
filterKey="roleFilters"
totalType="sum" totalType="sum"
></redaction-simple-doughnut-chart> ></redaction-simple-doughnut-chart>
</div> </div>

View File

@ -11,6 +11,7 @@ import {
IconButtonTypes, IconButtonTypes,
ListingComponent, ListingComponent,
LoadingService, LoadingService,
NestedFilter,
TableColumnConfig, TableColumnConfig,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
@ -18,6 +19,7 @@ import { map } from 'rxjs/operators';
import { rolesTranslations } from '../../../../translations/roles-translations'; import { rolesTranslations } from '../../../../translations/roles-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { User } from '@red/domain'; import { User } from '@red/domain';
import { userTypeChecker, userTypeFilters, UserTypes } from '../../../../utils';
@Component({ @Component({
templateUrl: './user-listing-screen.component.html', templateUrl: './user-listing-screen.component.html',
@ -110,38 +112,30 @@ export class UserListingScreenComponent extends ListingComponent<User> implement
private _computeStats() { private _computeStats() {
this.chartData = this._translateChartService.translateRoles( this.chartData = this._translateChartService.translateRoles(
[ UserTypes.map(type => ({
{ value: this.allEntities.filter(userTypeFilters[type]).length,
value: this.allEntities.filter(user => !user.isActive).length, color: type.replace('RED_', ''),
color: 'INACTIVE', label: type,
label: 'INACTIVE', key: type,
}, })).filter(type => type.value > 0),
{
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),
); );
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,
});
} }
} }

View File

@ -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 { Color } from '@utils/types';
import { FilterService } from '@iqser/common-ui'; import { FilterService, INestedFilter } from '@iqser/common-ui';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@ -12,14 +12,12 @@ export interface DoughnutChartConfig {
active?: boolean; active?: boolean;
} }
const FILTER_KEY = 'statusFilters';
@Component({ @Component({
selector: 'redaction-simple-doughnut-chart', selector: 'redaction-simple-doughnut-chart',
templateUrl: './simple-doughnut-chart.component.html', templateUrl: './simple-doughnut-chart.component.html',
styleUrls: ['./simple-doughnut-chart.component.scss'], styleUrls: ['./simple-doughnut-chart.component.scss'],
}) })
export class SimpleDoughnutChartComponent implements OnChanges { export class SimpleDoughnutChartComponent implements OnChanges, OnInit {
@Input() subtitle: string; @Input() subtitle: string;
@Input() config: DoughnutChartConfig[] = []; @Input() config: DoughnutChartConfig[] = [];
@Input() radius = 85; @Input() radius = 85;
@ -27,7 +25,8 @@ export class SimpleDoughnutChartComponent implements OnChanges {
@Input() direction: 'row' | 'column' = 'column'; @Input() direction: 'row' | 'column' = 'column';
@Input() totalType: 'sum' | 'count' = 'sum'; @Input() totalType: 'sum' | 'count' = 'sum';
@Input() counterText: string; @Input() counterText: string;
readonly filtersEnabled: boolean; @Input() filterKey = 'statusFilters';
filtersEnabled: boolean;
chartData: any[] = []; chartData: any[] = [];
perimeter: number; perimeter: number;
@ -35,10 +34,12 @@ export class SimpleDoughnutChartComponent implements OnChanges {
cy = 0; cy = 0;
size = 0; size = 0;
readonly statusFilters$ = this.filterService.getFilterModels$(FILTER_KEY) ?? of([]); filters$: Observable<INestedFilter[]>;
constructor(readonly filterService: FilterService) { 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 { get circumference(): number {
@ -53,6 +54,10 @@ export class SimpleDoughnutChartComponent implements OnChanges {
return this.totalType === 'sum' ? this.dataTotal : this.config.length; return this.totalType === 'sum' ? this.dataTotal : this.config.length;
} }
ngOnInit() {
this.filters$ = this.filterService.getFilterModels$(this.filterKey) ?? of([]);
}
ngOnChanges(): void { ngOnChanges(): void {
this.calculateChartData(); this.calculateChartData();
this.cx = this.radius + this.strokeWidth / 2; this.cx = this.radius + this.strokeWidth / 2;
@ -61,7 +66,7 @@ export class SimpleDoughnutChartComponent implements OnChanges {
} }
filterChecked$(key: string): Observable<boolean> { filterChecked$(key: string): Observable<boolean> {
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() { calculateChartData() {
@ -96,7 +101,7 @@ export class SimpleDoughnutChartComponent implements OnChanges {
selectValue(key: string): void { selectValue(key: string): void {
if (this.filtersEnabled) { if (this.filtersEnabled) {
this.filterService.toggleFilter(FILTER_KEY, key); this.filterService.toggleFilter(this.filterKey, key);
} }
} }
} }

View File

@ -1,5 +1,5 @@
import { File } from '@models/file/file'; import { File } from '@models/file/file';
import { Dossier } from '@red/domain'; import { Dossier, User } from '@red/domain';
import { handleCheckedValue, INestedFilter } from '@iqser/common-ui'; import { handleCheckedValue, INestedFilter } from '@iqser/common-ui';
export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) { 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 dossierTemplateChecker = (dw: Dossier, filter: INestedFilter) => dw.dossierTemplateId === filter.id;
export const dossierApproverChecker = (dw: Dossier, filter: INestedFilter) => dw.approverIds.includes(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);

View File

@ -23,7 +23,7 @@ export class User implements IUser, IListable {
this.firstName = user.firstName; this.firstName = user.firstName;
this.lastName = user.lastName; this.lastName = user.lastName;
this.name = this.firstName && this.lastName ? `${this.firstName} ${this.lastName}` : this.username; 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 { hasRole(role: string): boolean {