Refactor user listing

This commit is contained in:
Adina Țeudan 2021-04-19 22:59:49 +03:00
parent 271e8b6501
commit f3dca378e2
8 changed files with 45 additions and 81 deletions

View File

@ -15,7 +15,7 @@
<div class="content-container">
<div class="header-item">
<span class="all-caps-label">
{{ 'default-colors-screen.table-header.title' | translate: { length: colors.length } }}
{{ 'default-colors-screen.table-header.title' | translate: { length: allEntities.length } }}
</span>
</div>
@ -36,7 +36,7 @@
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div class="table-item" *cdkVirtualFor="let color of colors | sortBy: sortingOption.order:sortingOption.column">
<div class="table-item" *cdkVirtualFor="let color of allEntities | sortBy: sortingOption.order:sortingOption.column">
<div>
<div class="table-item-title heading" [translate]="'default-colors-screen.types.' + color.key"></div>
</div>

View File

@ -11,12 +11,11 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
templateUrl: './default-colors-screen.component.html',
styleUrls: ['./default-colors-screen.component.scss']
})
export class DefaultColorsScreenComponent extends BaseListingComponent {
export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: string; value: string }> {
protected readonly _sortKey = 'default-colors';
public viewReady = false;
private _colorsObj: Colors;
public colors: { key: string; value: string }[] = [];
constructor(
private readonly _appStateService: AppStateService,
@ -41,7 +40,7 @@ export class DefaultColorsScreenComponent extends BaseListingComponent {
.toPromise()
.then((data) => {
this._colorsObj = data;
this.colors = Object.keys(data).map((key) => ({
this.allEntities = Object.keys(data).map((key) => ({
key,
value: data[key]
}));

View File

@ -14,15 +14,13 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
templateUrl: './dictionary-listing-screen.component.html',
styleUrls: ['./dictionary-listing-screen.component.scss']
})
export class DictionaryListingScreenComponent extends BaseListingComponent implements OnInit {
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValue> implements OnInit {
protected readonly _searchKey = 'label';
protected readonly _selectionKey = 'type';
protected readonly _sortKey = 'dictionary-listing';
public viewReady = false;
public chartData: DoughnutChartConfig[] = [];
public allEntities: TypeValue[];
public displayedEntities: TypeValue[];
constructor(
private readonly _dialogService: AdminDialogService,

View File

@ -16,7 +16,6 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
protected readonly _selectionKey = 'id';
protected readonly _sortKey = 'file-attributes-listing';
public allEntities: FileAttributeConfig[] = [];
public viewReady = false;
public loading = false;

View File

@ -4,13 +4,14 @@ import { PermissionsService } from '../../../../services/permissions.service';
import { UserPreferenceService } from '../../../../services/user-preference.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { RuleSetModel } from '@redaction/red-ui-http';
@Component({
selector: 'redaction-rule-sets-listing-screen',
templateUrl: './rule-sets-listing-screen.component.html',
styleUrls: ['./rule-sets-listing-screen.component.scss']
})
export class RuleSetsListingScreenComponent extends BaseListingComponent implements OnInit {
export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSetModel> implements OnInit {
protected readonly _searchKey = 'name';
protected readonly _selectionKey = 'ruleSetId';
protected readonly _sortKey = 'rule-sets-listing';

View File

@ -28,23 +28,23 @@
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllUsersSelected"
[class.active]="areAllEntitiesSelected"
class="select-oval always-visible"
*ngIf="!areAllUsersSelected && !areSomeUsersSelected"
*ngIf="!areAllEntitiesSelected && !areSomeEntitiesSelected"
></div>
<mat-icon *ngIf="areAllUsersSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon *ngIf="areAllEntitiesSelected" (click)="toggleSelectAll()" class="selection-icon active" svgIcon="red:radio-selected"></mat-icon>
<mat-icon
*ngIf="areSomeUsersSelected && !areAllUsersSelected"
*ngIf="areSomeEntitiesSelected && !areAllEntitiesSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
></mat-icon>
</div>
<span class="all-caps-label">
{{ 'user-listing.table-header.title' | translate: { length: displayedUsers.length } }}
{{ 'user-listing.table-header.title' | translate: { length: displayedEntities.length } }}
</span>
<ng-container *ngIf="areSomeUsersSelected && !loading">
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
<redaction-circle-button
(action)="bulkDelete()"
[disabled]="!canDeleteSelected"
@ -73,14 +73,14 @@
<div class="scrollbar-placeholder"></div>
</div>
<redaction-empty-state *ngIf="!displayedUsers.length" screen="user-listing" type="no-match"></redaction-empty-state>
<redaction-empty-state *ngIf="!displayedEntities.length" screen="user-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div class="table-item" *cdkVirtualFor="let user of displayedUsers">
<div class="pr-0" (click)="toggleUserSelected($event, user)">
<div *ngIf="!isUserSelected(user)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isUserSelected(user)" svgIcon="red:radio-selected"></mat-icon>
<div class="table-item" *cdkVirtualFor="let user of displayedEntities">
<div class="pr-0" (click)="toggleEntitySelected($event, user)">
<div *ngIf="!isEntitySelected(user)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isEntitySelected(user)" svgIcon="red:radio-selected"></mat-icon>
</div>
<div>
<redaction-initials-avatar [user]="user" [withName]="true" [showYou]="true"></redaction-initials-avatar>

View File

@ -1,53 +1,44 @@
import { Component, OnInit } from '@angular/core';
import { Component, Injector, OnInit } from '@angular/core';
import { PermissionsService } from '../../../../services/permissions.service';
import { UserService } from '../../../../services/user.service';
import { User, UserControllerService } from '@redaction/red-ui-http';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../../utils/debounce';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { TranslateChartService } from '../../../../services/translate-chart.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
selector: 'redaction-user-listing-screen',
templateUrl: './user-listing-screen.component.html',
styleUrls: ['./user-listing-screen.component.scss']
})
export class UserListingScreenComponent implements OnInit {
export class UserListingScreenComponent extends BaseListingComponent<User> implements OnInit {
protected readonly _selectionKey = 'userId';
public viewReady = false;
public loading = false;
public collapsedDetails = false;
public chartData: DoughnutChartConfig[] = [];
public users: User[];
public displayedUsers: User[] = [];
public searchForm: FormGroup;
public selectedUsersIds: string[] = [];
constructor(
public readonly permissionsService: PermissionsService,
public readonly userService: UserService,
private readonly _formBuilder: FormBuilder,
private readonly _translateService: TranslateService,
private readonly _adminDialogService: AdminDialogService,
private readonly _userControllerService: UserControllerService,
private readonly _translateChartService: TranslateChartService
private readonly _translateChartService: TranslateChartService,
protected readonly _injector: Injector
) {
this.searchForm = this._formBuilder.group({
query: ['']
});
this.searchForm.valueChanges.subscribe(() => this._executeSearch());
super(_injector);
}
public async ngOnInit() {
await this._loadData();
}
@debounce(200)
private _executeSearch() {
const value = this.searchForm.get('query').value;
this.displayedUsers = this.users.filter((user) => this.userService.getName(user).toLowerCase().includes(value.toLowerCase()));
protected _searchField(user: any): string {
return this.userService.getName(user);
}
public openAddEditUserDialog($event: MouseEvent, user?: User) {
@ -77,7 +68,7 @@ export class UserListingScreenComponent implements OnInit {
}
private async _loadData() {
this.users = (await this._userControllerService.getAllUsers({ requestId: new Date().toISOString() }).toPromise()).users;
this.allEntities = (await this._userControllerService.getAllUsers({ requestId: new Date().toISOString() }).toPromise()).users;
this._executeSearch();
this._computeStats();
this.viewReady = true;
@ -88,32 +79,32 @@ export class UserListingScreenComponent implements OnInit {
this.chartData = this._translateChartService.translateRoles(
[
{
value: this.users.filter((user) => !this.userService.isActive(user)).length,
value: this.allEntities.filter((user) => !this.userService.isActive(user)).length,
color: 'INACTIVE',
label: 'INACTIVE'
},
{
value: this.users.filter((user) => user.roles.length === 1 && user.roles[0] === 'RED_USER').length,
value: this.allEntities.filter((user) => user.roles.length === 1 && user.roles[0] === 'RED_USER').length,
color: 'REGULAR',
label: 'REGULAR'
},
{
value: this.users.filter((user) => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
value: this.allEntities.filter((user) => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
color: 'MANAGER',
label: 'RED_MANAGER'
},
{
value: this.users.filter((user) => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
value: this.allEntities.filter((user) => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
color: 'MANAGER_ADMIN',
label: 'MANAGER_ADMIN'
},
{
value: this.users.filter((user) => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
value: this.allEntities.filter((user) => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
color: 'USER_ADMIN',
label: 'RED_USER_ADMIN'
},
{
value: this.users.filter((user) => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
value: this.allEntities.filter((user) => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
color: 'ADMIN',
label: 'RED_ADMIN'
}
@ -136,41 +127,11 @@ export class UserListingScreenComponent implements OnInit {
this.collapsedDetails = !this.collapsedDetails;
}
toggleUserSelected($event: MouseEvent, user: User) {
$event.stopPropagation();
const idx = this.selectedUsersIds.indexOf(user.userId);
if (idx === -1) {
this.selectedUsersIds.push(user.userId);
} else {
this.selectedUsersIds.splice(idx, 1);
}
}
public toggleSelectAll() {
if (this.areSomeUsersSelected) {
this.selectedUsersIds = [];
} else {
this.selectedUsersIds = this.displayedUsers.map((user) => user.userId);
}
}
public get areAllUsersSelected() {
return this.displayedUsers.length !== 0 && this.selectedUsersIds.length === this.displayedUsers.length;
}
public get areSomeUsersSelected() {
return this.selectedUsersIds.length > 0;
}
public isUserSelected(user: User) {
return this.selectedUsersIds.indexOf(user.userId) !== -1;
}
public async bulkDelete() {
this.openDeleteUserDialog(this.users.filter((u) => this.isUserSelected(u)));
this.openDeleteUserDialog(this.allEntities.filter((u) => this.isEntitySelected(u)));
}
public get canDeleteSelected(): boolean {
return this.selectedUsersIds.indexOf(this.userService.userId) === -1;
return this.selectedEntitiesIds.indexOf(this.userService.userId) === -1;
}
}

View File

@ -39,6 +39,10 @@ export class BaseListingComponent<T = any> {
protected get filterComponents(): FilterComponent[] {
return [];
}
protected _searchField(entity: T): string {
return entity[this.searchKey];
}
// ----
constructor(protected readonly _injector: Injector) {
@ -82,9 +86,11 @@ export class BaseListingComponent<T = any> {
@debounce(200)
protected _executeSearch() {
this.displayedEntities = (this.filters.length ? this.filteredEntities : this.allEntities).filter((entity) =>
entity[this.searchKey].toLowerCase().includes(this.searchForm.get('query').value.toLowerCase())
this._searchField(entity).toLowerCase().includes(this.searchForm.get('query').value.toLowerCase())
);
this.selectedEntitiesIds = this.displayedEntities.map((entity) => entity[this.selectionKey]).filter((id) => this.selectedEntitiesIds.includes(id));
if (this._selectionKey) {
this.selectedEntitiesIds = this.displayedEntities.map((entity) => entity[this.selectionKey]).filter((id) => this.selectedEntitiesIds.includes(id));
}
}
// Filter