Refactor dictionary listing

This commit is contained in:
Adina Țeudan 2021-04-19 22:14:25 +03:00
parent 7951e2e2cf
commit 1cc41f863b
2 changed files with 40 additions and 96 deletions

View File

@ -17,13 +17,13 @@
<div class="select-all-container">
<div
(click)="toggleSelectAll()"
[class.active]="areAllDictsSelected"
[class.active]="areAllEntitiesSelected"
class="select-oval always-visible"
*ngIf="!areAllDictsSelected && !areSomeDictsSelected"
*ngIf="!areAllEntitiesSelected && !areSomeEntitiesSelected"
></div>
<mat-icon *ngIf="areAllDictsSelected" (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="areSomeDictsSelected && !areAllDictsSelected"
*ngIf="areSomeEntitiesSelected && !areAllEntitiesSelected"
(click)="toggleSelectAll()"
class="selection-icon"
svgIcon="red:radio-indeterminate"
@ -31,7 +31,7 @@
</div>
<span class="all-caps-label">
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedDictionaries.length } }}
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedEntities.length } }}
</span>
<div class="attributes-actions-container">
@ -48,7 +48,7 @@
</div>
</div>
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!dictionaries.length">
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!allEntities.length">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -74,28 +74,24 @@
</div>
<redaction-empty-state
*ngIf="!dictionaries.length"
*ngIf="!allEntities.length"
icon="red:dictionary"
(action)="openAddEditDictionaryDialog()"
[showButton]="permissionsService.isAdmin()"
screen="dictionary-listing"
></redaction-empty-state>
<redaction-empty-state
*ngIf="dictionaries.length && !displayedDictionaries.length"
screen="dictionary-listing"
type="no-match"
></redaction-empty-state>
<redaction-empty-state *ngIf="allEntities.length && !displayedEntities.length" screen="dictionary-listing" type="no-match"></redaction-empty-state>
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<div
class="table-item pointer"
*cdkVirtualFor="let dict of displayedDictionaries | sortBy: sortingOption.order:sortingOption.column"
*cdkVirtualFor="let dict of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
[routerLink]="[dict.type]"
>
<div class="pr-0" (click)="toggleDictSelected($event, dict)">
<div *ngIf="!isDictSelected(dict)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isDictSelected(dict)" svgIcon="red:radio-selected"></mat-icon>
<div class="pr-0" (click)="toggleEntitySelected($event, dict)">
<div *ngIf="!isEntitySelected(dict)" class="select-oval"></div>
<mat-icon class="selection-icon active" *ngIf="isEntitySelected(dict)" svgIcon="red:radio-selected"></mat-icon>
</div>
<div>
@ -137,7 +133,7 @@
</redaction-circle-button>
<redaction-circle-button
(action)="openEditDictionaryDialog($event, dict)"
(action)="openAddEditDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
tooltip="dictionary-listing.action.edit"
type="dark-bg"
@ -153,7 +149,7 @@
<div class="right-container" redactionHasScrollbar>
<redaction-simple-doughnut-chart
*ngIf="dictionaries.length"
*ngIf="allEntities.length"
[config]="chartData"
[strokeWidth]="15"
[radius]="82"
@ -164,3 +160,5 @@
</div>
</div>
</section>
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>

View File

@ -1,71 +1,58 @@
import { Component, OnInit } from '@angular/core';
import { Component, Injector, OnInit } from '@angular/core';
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
import { SortingOption, SortingService } from '../../../../services/sorting.service';
import { AppStateService } from '../../../../state/app-state.service';
import { tap } from 'rxjs/operators';
import { forkJoin } from 'rxjs';
import { PermissionsService } from '../../../../services/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../../utils/debounce';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
selector: 'redaction-dictionary-listing-screen',
templateUrl: './dictionary-listing-screen.component.html',
styleUrls: ['./dictionary-listing-screen.component.scss']
})
export class DictionaryListingScreenComponent implements OnInit {
export class DictionaryListingScreenComponent extends BaseListingComponent implements OnInit {
protected readonly _searchKey = 'label';
protected readonly _selectionKey = 'type';
protected readonly _sortKey = 'dictionary-listing';
public viewReady = false;
public chartData: DoughnutChartConfig[] = [];
public dictionaries: TypeValue[];
public displayedDictionaries: TypeValue[];
public selectedDictKeys: string[] = [];
public searchForm: FormGroup;
public allEntities: TypeValue[];
public displayedEntities: TypeValue[];
constructor(
private readonly _dialogService: AdminDialogService,
private readonly _sortingService: SortingService,
private readonly _formBuilder: FormBuilder,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _appStateService: AppStateService,
public readonly permissionsService: PermissionsService
public readonly permissionsService: PermissionsService,
protected readonly _injector: Injector
) {
super(_injector);
this._appStateService.activateRuleSet(_activatedRoute.snapshot.params.ruleSetId);
this.searchForm = this._formBuilder.group({
query: ['']
});
this.searchForm.valueChanges.subscribe((value) => this._executeSearch(value));
}
ngOnInit(): void {
this._loadDictionaryData();
this._calculateData();
}
@debounce(200)
private _executeSearch(value: { query: string }) {
this.displayedDictionaries = this.dictionaries.filter((dict) => dict.label.toLowerCase().includes(value.query.toLowerCase()));
}
private _loadDictionaryData() {
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
this.dictionaries = Object.keys(appStateDictionaryData)
this.allEntities = Object.keys(appStateDictionaryData)
.map((key) => appStateDictionaryData[key])
.filter((d) => !d.virtual || d.type === 'false_positive');
this.displayedDictionaries = [...this.dictionaries];
const dataObs = [];
this.dictionaries.forEach((item) => {
const observable = this._dictionaryControllerService.getDictionaryForType(item.type, this._appStateService.activeRuleSetId).pipe(
this.displayedEntities = [...this.allEntities];
const dataObs = this.allEntities.map((dict) =>
this._dictionaryControllerService.getDictionaryForType(dict.type, this._appStateService.activeRuleSetId).pipe(
tap((values) => {
item.entries = values.entries ? values.entries : [];
dict.entries = values.entries ? values.entries : [];
})
);
dataObs.push(observable);
});
)
);
forkJoin(dataObs).subscribe(() => {
this._calculateData();
});
@ -73,7 +60,7 @@ export class DictionaryListingScreenComponent implements OnInit {
private _calculateData() {
this.chartData = [];
for (const dict of this.dictionaries) {
for (const dict of this.allEntities) {
this.chartData.push({
value: dict.entries ? dict.entries.length : 0,
color: dict.hexColor,
@ -82,47 +69,11 @@ export class DictionaryListingScreenComponent implements OnInit {
});
}
this.chartData.sort((a, b) => (a.label < b.label ? -1 : 1));
this.viewReady = true;
}
public get sortingOption(): SortingOption {
return this._sortingService.getSortingOption('dictionary-listing');
}
public toggleSort($event) {
this._sortingService.toggleSort('dictionary-listing', $event);
}
toggleDictSelected($event: MouseEvent, dict: TypeValue) {
$event.stopPropagation();
const idx = this.selectedDictKeys.indexOf(dict.type);
if (idx === -1) {
this.selectedDictKeys.push(dict.type);
} else {
this.selectedDictKeys.splice(idx, 1);
}
}
public toggleSelectAll() {
if (this.areSomeDictsSelected) {
this.selectedDictKeys = [];
} else {
this.selectedDictKeys = this.displayedDictionaries.map((dict) => dict.type);
}
}
public get areAllDictsSelected() {
return this.displayedDictionaries.length !== 0 && this.selectedDictKeys.length === this.displayedDictionaries.length;
}
public get areSomeDictsSelected() {
return this.selectedDictKeys.length > 0;
}
public isDictSelected(dict: TypeValue) {
return this.selectedDictKeys.indexOf(dict.type) !== -1;
}
openAddEditDictionaryDialog(dict?: TypeValue) {
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
$event?.stopPropagation();
this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
if (newDictionary) {
await this._appStateService.loadDictionaryData();
@ -131,11 +82,6 @@ export class DictionaryListingScreenComponent implements OnInit {
});
}
openEditDictionaryDialog($event: any, dict: TypeValue) {
$event.stopPropagation();
this.openAddEditDictionaryDialog(dict);
}
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
await this._appStateService.loadDictionaryData();