change base listing component

This commit is contained in:
Dan Percic 2021-07-14 15:30:06 +03:00
parent 6c21b6e957
commit bb53a1a6dc
18 changed files with 147 additions and 311 deletions

View File

@ -3,7 +3,7 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="areSomeEntitiesSelected && !areAllEntitiesSelected"
[indeterminate]="(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
@ -13,22 +13,20 @@
}}
</span>
<ng-container *ngIf="areSomeEntitiesSelected">
<ng-container *ngIf="areSomeEntitiesSelected$ | async">
<redaction-circle-button
[matMenuTriggerFor]="readOnlyMenu"
icon="red:read-only"
tooltip="file-attributes-csv-import.table-header.actions.read-only"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<redaction-circle-button
(action)="deactivateSelection()"
icon="red:trash"
tooltip="file-attributes-csv-import.table-header.actions.remove-selected"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<div class="separator"></div>
@ -92,7 +90,7 @@
</div>
<redaction-empty-state
*ngIf="!allEntities.length"
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:attribute"
screen="file-attributes-csv-import"
></redaction-empty-state>
@ -102,7 +100,7 @@
<div
(mouseenter)="setHoveredColumn.emit(field.csvColumn)"
(mouseleave)="setHoveredColumn.emit()"
*cdkVirtualFor="let field of displayedEntities"
*cdkVirtualFor="let field of displayedEntities$ | async"
class="table-item"
>
<div (click)="toggleEntitySelected($event, field)" class="selection-column">
@ -127,23 +125,20 @@
icon="red:edit"
tooltip="file-attributes-csv-import.action.edit-name"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<ng-container *ngIf="field.editingName">
<redaction-circle-button
(action)="field.editingName = false; field.name = field.temporaryName"
icon="red:check"
tooltip="file-attributes-csv-import.action.save-name"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<redaction-circle-button
(action)="field.editingName = false; field.temporaryName = field.name"
icon="red:close"
tooltip="file-attributes-csv-import.action.cancel-edit-name"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
</ng-container>
</div>
<div>
@ -174,8 +169,7 @@
icon="red:trash"
tooltip="file-attributes-csv-import.action.remove"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>

View File

@ -7,13 +7,13 @@ import {
Output,
SimpleChanges
} from '@angular/core';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { Field } from '../file-attributes-csv-import-dialog.component';
import { FileAttributeConfig } from '@redaction/red-ui-http';
import { FilterService } from '../../../../shared/services/filter.service';
import { SearchService } from '../../../../shared/services/search.service';
import { ScreenStateService } from '../../../../shared/services/screen-state.service';
import { SortingService } from '../../../../../services/sorting.service';
import { BaseListingComponent } from '../../../../shared/base/base-listing.component';
@Component({
selector: 'redaction-active-fields-listing',
@ -22,8 +22,8 @@ import { SortingService } from '../../../../../services/sorting.service';
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class ActiveFieldsListingComponent extends BaseListingComponent<Field> implements OnChanges {
@Input() allEntities: Field[];
@Output() allEntitiesChange = new EventEmitter<Field[]>();
@Input() entities: Field[];
@Output() entitiesChange = new EventEmitter<Field[]>();
@Output() setHoveredColumn = new EventEmitter<string>();
@Output() toggleFieldActive = new EventEmitter<Field>();
@ -33,16 +33,16 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
FileAttributeConfig.TypeEnum.DATE
];
protected readonly _selectionKey = 'csvColumn';
constructor(protected readonly _injector: Injector) {
super(_injector);
this._screenStateService.setIdKey('csvColumn');
}
ngOnChanges(changes: SimpleChanges): void {
if (changes.allEntities) {
this.displayedEntities = this.allEntities;
this._updateSelection();
if (changes.entities) {
this._screenStateService.setEntities(this.entities);
this._screenStateService.setDisplayedEntities(this.entities);
this._screenStateService.updateSelection();
}
}
@ -50,13 +50,15 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
this.allEntities
.filter(field => this.isSelected(field))
.forEach(field => (field.primaryAttribute = false));
this.allEntities = [...this.allEntities.filter(field => !this.isSelected(field))];
this.allEntitiesChange.emit(this.allEntities);
this.selectedEntitiesIds = [];
this._screenStateService.setEntities([
...this.allEntities.filter(field => !this.isSelected(field))
]);
this.entitiesChange.emit(this.allEntities);
this._screenStateService.setSelectedEntitiesIds([]);
}
setAttributeForSelection(attribute: string, value: any) {
for (const csvColumn of this.selectedEntitiesIds) {
for (const csvColumn of this._screenStateService.selectedEntitiesIds) {
this.allEntities.find(f => f.csvColumn === csvColumn)[attribute] = value;
}
}

View File

@ -119,7 +119,7 @@
(click)="toggleFieldActive(field)"
(mouseenter)="setHoveredColumn(field.csvColumn)"
(mouseleave)="setHoveredColumn()"
*ngFor="let field of displayedEntities"
*ngFor="let field of displayedEntities$ | async"
class="csv-header-pill-wrapper"
>
<div [class.selected]="isActive(field)" class="csv-header-pill">
@ -177,7 +177,7 @@
<redaction-active-fields-listing
(setHoveredColumn)="setHoveredColumn($event)"
(toggleFieldActive)="toggleFieldActive($event)"
[(allEntities)]="activeFields"
[(entities)]="activeFields"
></redaction-active-fields-listing>
</div>
</div>

View File

@ -1,5 +1,5 @@
import { Component, Inject, Injector } from '@angular/core';
import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { AbstractControl, FormBuilder, FormGroup, ValidatorFn, Validators } from '@angular/forms';
import { AppStateService } from '@state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import * as Papa from 'papaparse';
@ -10,13 +10,13 @@ import {
} from '@redaction/red-ui-http';
import { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
export interface Field {
id?: string;
@ -55,6 +55,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
private readonly _fileAttributesControllerService: FileAttributesControllerService,
private readonly _translateService: TranslateService,
private readonly _notificationService: NotificationService,
private readonly _formBuilder: FormBuilder,
public dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
protected readonly _injector: Injector,
@Inject(MAT_DIALOG_DATA)
@ -102,10 +103,10 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
this.parseResult.meta.fields = Object.keys(this.parseResult.data[0]);
}
this.allEntities = this.parseResult.meta.fields.map(field =>
this._buildAttribute(field)
this._screenStateService.setEntities(
this.parseResult.meta.fields.map(field => this._buildAttribute(field))
);
this.displayedEntities = [...this.allEntities];
this._screenStateService.setDisplayedEntities(this.allEntities);
this.activeFields = [];
for (const entity of this.allEntities) {
@ -175,9 +176,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
}
}
return count;
} else {
return 0;
}
return 0;
}
isActive(field: Field): boolean {

View File

@ -24,7 +24,7 @@
<span class="all-caps-label">
{{
'default-colors-screen.table-header.title'
| translate: { length: allEntities.length }
| translate: { length: (allEntities$ | async).length }
}}
</span>
</div>
@ -51,7 +51,9 @@
<!-- Table lines -->
<div
*cdkVirtualFor="
let color of allEntities | sortBy: sortingOption.order:sortingOption.column
let color of allEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
"
class="table-item"
>
@ -77,8 +79,7 @@
icon="red:edit"
tooltip="default-colors-screen.action.edit"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>

View File

@ -4,12 +4,12 @@ import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
import { ActivatedRoute } from '@angular/router';
import { PermissionsService } from '@services/permissions.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { LoadingService } from '../../../../services/loading.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
templateUrl: './default-colors-screen.component.html',
@ -64,10 +64,12 @@ export class DefaultColorsScreenComponent
.getColors(this._appStateService.activeDossierTemplateId)
.toPromise();
this._colorsObj = data;
this.allEntities = Object.keys(data).map(key => ({
key,
value: data[key]
}));
this._screenStateService.setEntities(
Object.keys(data).map(key => ({
key,
value: data[key]
}))
);
this._loadingService.stop();
}
}

View File

@ -7,7 +7,6 @@ import { forkJoin, of } from 'rxjs';
import { PermissionsService } from '@services/permissions.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '../../../../services/loading.service';
@ -15,7 +14,7 @@ import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
value: dict.entries ? dict.entries.length : 0,
@ -30,7 +29,7 @@ const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DictionaryListingScreenComponent
extends NewBaseListingComponent<TypeValueWrapper>
extends BaseListingComponent<TypeValueWrapper>
implements OnInit
{
chartData: DoughnutChartConfig[] = [];

View File

@ -10,7 +10,7 @@ import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
templateUrl: './dossier-templates-listing-screen.component.html',
@ -19,7 +19,7 @@ import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.c
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DossierTemplatesListingScreenComponent
extends NewBaseListingComponent<DossierTemplateModelWrapper>
extends BaseListingComponent<DossierTemplateModelWrapper>
implements OnInit
{
constructor(

View File

@ -25,17 +25,22 @@
<redaction-round-checkbox
(click)="toggleSelectAll()"
[active]="areAllEntitiesSelected"
[indeterminate]="areSomeEntitiesSelected && !areAllEntitiesSelected"
[indeterminate]="
(areSomeEntitiesSelected$ | async) && !areAllEntitiesSelected
"
></redaction-round-checkbox>
</div>
<span class="all-caps-label">
{{ 'file-attributes-listing.table-header.title' | translate: { length: displayedEntities.length } }}
{{
'file-attributes-listing.table-header.title'
| translate: { length: (displayedEntities$ | async)?.length }
}}
</span>
<redaction-circle-button
(click)="openConfirmDeleteAttributeDialog($event)"
*ngIf="areSomeEntitiesSelected"
*ngIf="areSomeEntitiesSelected$ | async"
icon="red:trash"
tooltip="file-attributes-listing.bulk-actions.delete"
type="dark-bg"
@ -67,7 +72,11 @@
</div>
</div>
<div [class.no-data]="!allEntities.length" class="table-header" redactionSyncWidth="table-item">
<div
[class.no-data]="(allEntities$ | async)?.length === 0"
class="table-header"
redactionSyncWidth="table-item"
>
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -110,13 +119,13 @@
</div>
<redaction-empty-state
*ngIf="!allEntities.length"
*ngIf="(allEntities$ | async)?.length === 0"
icon="red:attribute"
screen="file-attributes-listing"
></redaction-empty-state>
<redaction-empty-state
*ngIf="allEntities.length && !displayedEntities.length"
*ngIf="(allEntities$ | async)?.length && (displayedEntities$ | async)?.length === 0"
screen="file-attributes-listing"
type="no-match"
></redaction-empty-state>
@ -124,7 +133,11 @@
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
<!-- Table lines -->
<div
*cdkVirtualFor="let attribute of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
*cdkVirtualFor="
let attribute of displayedEntities$
| async
| sortBy: sortingOption.order:sortingOption.column
"
class="table-item"
>
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">

View File

@ -1,26 +1,34 @@
import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
ElementRef,
Injector,
OnInit,
ViewChild
} from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '@state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { LoadingService } from '../../../../services/loading.service';
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
templateUrl: './file-attributes-listing-screen.component.html',
styleUrls: ['./file-attributes-listing-screen.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit {
protected readonly _searchKey = 'label';
protected readonly _selectionKey = 'id';
export class FileAttributesListingScreenComponent
extends BaseListingComponent<FileAttributeConfig>
implements OnInit
{
private _existingConfiguration: FileAttributesConfig;
@ViewChild('fileInput') private _fileInput: ElementRef;
constructor(
@ -34,9 +42,9 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
) {
super(_injector);
this._sortingService.setScreenName(ScreenNames.FILE_ATTRIBUTES_LISTING);
this._appStateService.activateDossierTemplate(
_activatedRoute.snapshot.params.dossierTemplateId
);
this._searchService.setSearchKey('label');
this._screenStateService.setIdKey('id');
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
async ngOnInit() {
@ -67,7 +75,10 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
.toPromise();
} else {
await this._fileAttributesService
.deleteFileAttributes(this.selectedEntitiesIds, this._appStateService.activeDossierTemplateId)
.deleteFileAttributes(
this._screenStateService.selectedEntitiesIds,
this._appStateService.activeDossierTemplateId
)
.toPromise();
}
await this._loadData();
@ -86,9 +97,7 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
dossierTemplateId: this._appStateService.activeDossierTemplateId,
existingConfiguration: this._existingConfiguration
},
async () => {
await this._loadData();
}
async () => await this._loadData()
);
}
@ -99,10 +108,10 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
.getFileAttributesConfiguration(this._appStateService.activeDossierTemplateId)
.toPromise();
this._existingConfiguration = response;
this.allEntities = response?.fileAttributeConfigs || [];
this._screenStateService.setEntities(response?.fileAttributeConfigs || []);
} catch (e) {
} finally {
this._executeSearchImmediately();
this.filterService.filterEntities();
this._loadingService.stop();
}
}

View File

@ -114,12 +114,6 @@
>
{{ entity.dossierName }}
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:template"></mat-icon>
{{ getDossierTemplate(entity.dossierTemplateId).name }}
</div>
</div>
<div class="small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:user"></mat-icon>
@ -144,7 +138,7 @@
</div>
<div class="small-label">
{{ entity.softDeletedTime | date: 'd MMM. yyyy hh:mm' }}
{{ entity.softDeletedTime | date: 'd MMM. yyyy, hh:mm a' }}
</div>
<div>

View File

@ -30,7 +30,7 @@ redaction-table-col-name::ng-deep {
}
> div {
height: 90px;
height: 80px;
padding: 0 24px;
}
}

View File

@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { Dossier, DossierTemplateModel } from '@redaction/red-ui-http';
import { Dossier, DossierTemplateModel, StatusControllerService } from '@redaction/red-ui-http';
import { LoadingService } from '../../../../services/loading.service';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import * as moment from 'moment';
@ -9,8 +9,9 @@ import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { tap } from 'rxjs/operators';
@Component({
templateUrl: './trash-screen.component.html',
@ -18,7 +19,7 @@ import { DossiersService } from '../../../dossier/services/dossiers.service';
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService, DossiersService]
})
export class TrashScreenComponent extends NewBaseListingComponent<Dossier> implements OnInit {
export class TrashScreenComponent extends BaseListingComponent<Dossier> implements OnInit {
readonly itemSize = 85;
private readonly _deleteRetentionHours = this._appConfigService.getConfig(
AppConfigKey.DELETE_RETENTION_HOURS
@ -26,6 +27,7 @@ export class TrashScreenComponent extends NewBaseListingComponent<Dossier> imple
constructor(
private readonly _appStateService: AppStateService,
private readonly _statusControllerService: StatusControllerService,
readonly permissionsService: PermissionsService,
protected readonly _injector: Injector,
private readonly _dossiersService: DossiersService,
@ -50,10 +52,6 @@ export class TrashScreenComponent extends NewBaseListingComponent<Dossier> imple
this._screenStateService.setEntities(await this._dossiersService.getDeletedDossiers());
}
getDossierTemplate(dossierTemplateId: string): DossierTemplateModel {
return this._appStateService.getDossierTemplateById(dossierTemplateId);
}
getRestoreDate(softDeletedTime: string): string {
return moment(softDeletedTime).add(this._deleteRetentionHours, 'hours').toISOString();
}

View File

@ -19,7 +19,7 @@ import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { SortingService } from '../../../../services/sorting.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -29,7 +29,7 @@ import { map } from 'rxjs/operators';
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class UserListingScreenComponent extends NewBaseListingComponent<User> implements OnInit {
export class UserListingScreenComponent extends BaseListingComponent<User> implements OnInit {
collapsedDetails = false;
chartData: DoughnutChartConfig[] = [];
@ViewChildren(InitialsAvatarComponent)

View File

@ -35,7 +35,7 @@ import { ButtonConfig } from '../../../shared/components/page-header/models/butt
import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
@Component({
@ -45,7 +45,7 @@ import { ScreenNames, SortingService } from '../../../../services/sorting.servic
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DossierListingScreenComponent
extends NewBaseListingComponent<DossierWrapper>
extends BaseListingComponent<DossierWrapper>
implements OnInit, OnDestroy, OnAttach, OnDetach
{
dossiersChartData: DoughnutChartConfig[] = [];

View File

@ -43,7 +43,7 @@ import { FilterService } from '../../../shared/services/filter.service';
import { SearchService } from '../../../shared/services/search.service';
import { ScreenStateService } from '../../../shared/services/screen-state.service';
import { ScreenNames, SortingService } from '../../../../services/sorting.service';
import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.component';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
@Component({
templateUrl: './dossier-overview-screen.component.html',
@ -51,7 +51,7 @@ import { NewBaseListingComponent } from '../../../shared/base/new-base-listing.c
providers: [FilterService, SearchService, ScreenStateService, SortingService]
})
export class DossierOverviewScreenComponent
extends NewBaseListingComponent<FileStatusWrapper>
extends BaseListingComponent<FileStatusWrapper>
implements OnInit, OnDestroy, OnDetach, OnAttach
{
collapsedDetails = false;

View File

@ -1,177 +1,83 @@
import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Component, Injector, ViewChild } from '@angular/core';
import { SortingOption, SortingService } from '@services/sorting.service';
import { FilterModel } from '../components/filters/popup-filter/model/filter.model';
import { QuickFiltersComponent } from '../components/filters/quick-filters/quick-filters.component';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { FilterService } from '../services/filter.service';
import { SearchService } from '../services/search.service';
import { ScreenStateService } from '../services/screen-state.service';
import { getFilteredEntities } from '../components/filters/popup-filter/utils/filter-utils';
import { debounce } from '../../../utils/debounce';
import { FilterWrapper } from '../components/filters/popup-filter/model/filter-wrapper.model';
// Functionalities: Filter, search, select, sort
// Usage: overwrite necessary methods/members in your component
import { Observable } from 'rxjs';
import { FilterModel } from '../components/filters/popup-filter/model/filter.model';
@Component({ template: '' })
export abstract class BaseListingComponent<T = any> {
allEntities: T[] = [];
filteredEntities: T[] = [];
displayedEntities: T[] = [];
selectedEntitiesIds: string[] = [];
searchForm: FormGroup;
showResetFilters = false;
@ViewChild(CdkVirtualScrollViewport) scrollViewport: CdkVirtualScrollViewport;
export abstract class BaseListingComponent<T> {
@ViewChild(CdkVirtualScrollViewport)
readonly scrollViewport: CdkVirtualScrollViewport;
protected readonly _formBuilder: FormBuilder;
protected readonly _changeDetectorRef: ChangeDetectorRef;
readonly filterService: FilterService<T>;
protected readonly _sortingService: SortingService;
protected readonly _filterService: FilterService<T>;
protected readonly _searchService: SearchService<T>;
protected readonly _screenStateService: ScreenStateService<T>;
// ----
// Overwrite in child class:
protected readonly _searchKey: string;
protected readonly _selectionKey: string;
// Overwrite this in ngOnInit
@ViewChild(QuickFiltersComponent)
protected _quickFilters: QuickFiltersComponent<T>;
private _searchValue = '';
protected constructor(protected readonly _injector: Injector) {
this._formBuilder = this._injector.get<FormBuilder>(FormBuilder);
this._changeDetectorRef = this._injector.get<ChangeDetectorRef>(ChangeDetectorRef);
this.filterService = this._injector.get<FilterService<T>>(FilterService);
this._sortingService = this._injector.get<SortingService>(SortingService);
this._filterService = this._injector.get<FilterService<T>>(FilterService);
this._searchService = this._injector.get<SearchService<T>>(SearchService);
this._screenStateService = this._injector.get<ScreenStateService<T>>(ScreenStateService);
}
get areAllEntitiesSelected() {
return (
this.displayedEntities.length !== 0 &&
this.selectedEntitiesIds.length === this.displayedEntities.length
);
get selectedEntitiesIds$(): Observable<string[]> {
return this._screenStateService.selectedEntitiesIds$;
}
get areSomeEntitiesSelected() {
return this.selectedEntitiesIds.length > 0;
get displayedEntities$(): Observable<T[]> {
return this._screenStateService.displayedEntities$;
}
get allEntities$(): Observable<T[]> {
return this._screenStateService.entities$;
}
get allEntities(): T[] {
return this._screenStateService.entities;
}
get areAllEntitiesSelected() {
return this._screenStateService.areAllEntitiesSelected;
}
get areSomeEntitiesSelected$() {
return this._screenStateService.areSomeEntitiesSelected$;
}
get sortingOption(): SortingOption {
return this._sortingService.getSortingOption();
}
protected get _filters(): FilterWrapper[] {
return [];
getFilter$(slug: string): Observable<FilterModel[]> {
return this.filterService.getFilter$(slug);
}
// ----
private get _getSearchKey(): string {
if (!this._searchKey) throw new Error('Not implemented');
return this._searchKey;
}
// Search
private get _getSelectionKey(): string {
if (!this._selectionKey) throw new Error('Not implemented');
return this._selectionKey;
}
filtersChanged(filters?: { [key: string]: FilterModel[] } | FilterModel[]): void {
if (filters instanceof Array) this.showResetFilters = !!filters.find(f => f.checked);
else
for (const key of Object.keys(filters ?? {})) {
for (let idx = 0; idx < this[key]?.length; ++idx) {
this[key][idx] = filters[key][idx];
}
}
this._filterEntities();
get searchForm() {
return this._searchService.searchForm;
}
resetFilters() {
this.showResetFilters = false;
this.filtersChanged();
}
// Filter
toggleEntitySelected($event: MouseEvent, entity: T) {
$event.stopPropagation();
const idx = this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]);
if (idx === -1) {
this.selectedEntitiesIds.push(entity[this._getSelectionKey]);
} else {
this.selectedEntitiesIds.splice(idx, 1);
}
}
toggleSelectAll() {
if (this.areSomeEntitiesSelected) {
this.selectedEntitiesIds = [];
} else {
this.selectedEntitiesIds = this.displayedEntities.map(
entity => entity[this._getSelectionKey]
);
}
}
isSelected(entity: T) {
return this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]) !== -1;
this.filterService.reset();
}
toggleSort($event) {
this._sortingService.toggleSort($event);
}
// Selection
protected _preFilter() {
return;
toggleSelectAll() {
return this._screenStateService.toggleSelectAll();
}
protected _searchField(entity: T): string {
return entity[this._getSearchKey];
toggleEntitySelected(event: MouseEvent, entity: T) {
event.stopPropagation();
return this._screenStateService.toggleEntitySelected(entity);
}
@debounce(200)
executeSearch(value: string) {
this._searchValue = value;
this._executeSearchImmediately();
}
protected _executeSearchImmediately() {
this.displayedEntities = (
this._filters.length ? this.filteredEntities : this.allEntities
).filter(entity =>
this._searchField(entity).toLowerCase().includes(this._searchValue.toLowerCase())
);
this._updateSelection();
}
protected _updateSelection() {
if (this._selectionKey) {
this.selectedEntitiesIds = this.displayedEntities
.map(entity => entity[this._getSelectionKey])
.filter(id => this.selectedEntitiesIds.includes(id));
}
}
// Sort
protected _filterEntities() {
this._preFilter();
this.filteredEntities = getFilteredEntities(this.allEntities, this._filters);
this.executeSearch(this._searchValue);
this._changeDetectorRef.detectChanges();
isSelected(entity: T) {
return this._screenStateService.isSelected(entity);
}
}

View File

@ -1,83 +0,0 @@
import { Component, Injector, ViewChild } from '@angular/core';
import { SortingOption, SortingService } from '@services/sorting.service';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { FilterService } from '../services/filter.service';
import { SearchService } from '../services/search.service';
import { ScreenStateService } from '../services/screen-state.service';
import { Observable } from 'rxjs';
import { FilterModel } from '../components/filters/popup-filter/model/filter.model';
@Component({ template: '' })
export abstract class NewBaseListingComponent<T> {
@ViewChild(CdkVirtualScrollViewport)
readonly scrollViewport: CdkVirtualScrollViewport;
readonly filterService: FilterService<T>;
protected readonly _sortingService: SortingService;
protected readonly _searchService: SearchService<T>;
protected readonly _screenStateService: ScreenStateService<T>;
protected constructor(protected readonly _injector: Injector) {
this.filterService = this._injector.get<FilterService<T>>(FilterService);
this._sortingService = this._injector.get<SortingService>(SortingService);
this._searchService = this._injector.get<SearchService<T>>(SearchService);
this._screenStateService = this._injector.get<ScreenStateService<T>>(ScreenStateService);
}
get selectedEntitiesIds$(): Observable<string[]> {
return this._screenStateService.selectedEntitiesIds$;
}
get displayedEntities$(): Observable<T[]> {
return this._screenStateService.displayedEntities$;
}
get allEntities$(): Observable<T[]> {
return this._screenStateService.entities$;
}
get allEntities(): T[] {
return this._screenStateService.entities;
}
get areAllEntitiesSelected() {
return this._screenStateService.areAllEntitiesSelected;
}
get areSomeEntitiesSelected$() {
return this._screenStateService.areSomeEntitiesSelected$;
}
get sortingOption(): SortingOption {
return this._sortingService.getSortingOption();
}
getFilter$(slug: string): Observable<FilterModel[]> {
return this.filterService.getFilter$(slug);
}
get searchForm() {
return this._searchService.searchForm;
}
resetFilters() {
this.filterService.reset();
}
toggleSort($event) {
this._sortingService.toggleSort($event);
}
toggleSelectAll() {
return this._screenStateService.toggleSelectAll();
}
toggleEntitySelected(event: MouseEvent, entity: T) {
event.stopPropagation();
return this._screenStateService.toggleEntitySelected(entity);
}
isSelected(entity: T) {
return this._screenStateService.isSelected(entity);
}
}