move filters to classes, add searchKey to entities

This commit is contained in:
Dan Percic 2021-09-25 21:03:40 +03:00
parent d1cc15ec23
commit 5387fef462
17 changed files with 226 additions and 191 deletions

View File

@ -64,7 +64,6 @@ export class FileStatusWrapper implements FileStatus, IListable {
} }
} }
readonly excludedPagesCount = this.excludedPages?.length ?? 0;
readonly statusSort = StatusSorter[this.status]; readonly statusSort = StatusSorter[this.status];
readonly pages = this._pages; readonly pages = this._pages;
readonly cacheIdentifier = btoa(this.lastUploaded + this.lastOCRTime); readonly cacheIdentifier = btoa(this.lastUploaded + this.lastOCRTime);
@ -88,6 +87,10 @@ export class FileStatusWrapper implements FileStatus, IListable {
return this.fileId; return this.fileId;
} }
get searchKey(): string {
return this.filename;
}
private get _pages() { private get _pages() {
if (this.fileStatus.status === 'ERROR') { if (this.fileStatus.status === 'ERROR') {
return -1; return -1;

View File

@ -100,7 +100,7 @@
(click)="toggleFieldActive(field)" (click)="toggleFieldActive(field)"
(mouseenter)="setHoveredColumn(field.csvColumn)" (mouseenter)="setHoveredColumn(field.csvColumn)"
(mouseleave)="setHoveredColumn()" (mouseleave)="setHoveredColumn()"
*ngFor="let field of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey" *ngFor="let field of sortedDisplayedEntities$ | async"
class="csv-header-pill-wrapper" class="csv-header-pill-wrapper"
> >
<div [class.selected]="isActive(field)" class="csv-header-pill"> <div [class.selected]="isActive(field)" class="csv-header-pill">

View File

@ -50,7 +50,7 @@
<div *ngIf="hasFiles" class="mt-24 legend pb-32"> <div *ngIf="hasFiles" class="mt-24 legend pb-32">
<div <div
(click)="filterService.toggleFilter('needsWorkFilters', filter.key)" (click)="filterService.toggleFilter('needsWorkFilters', filter.id)"
*ngFor="let filter of needsWorkFilters$ | async" *ngFor="let filter of needsWorkFilters$ | async"
[class.active]="filter.checked" [class.active]="filter.checked"
> >

View File

@ -3,7 +3,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, IqserEventTarget, NestedFilter } from '@iqser/common-ui'; import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, INestedFilter, IqserEventTarget } from '@iqser/common-ui';
import { FileDataModel } from '@models/file/file-data.model'; import { FileDataModel } from '@models/file/file-data.model';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { WebViewerInstance } from '@pdftron/webviewer'; import { WebViewerInstance } from '@pdftron/webviewer';
@ -122,8 +122,8 @@ export class FileWorkloadComponent {
private _filterAnnotations( private _filterAnnotations(
annotations: AnnotationWrapper[], annotations: AnnotationWrapper[],
primary: NestedFilter[], primary: INestedFilter[],
secondary: NestedFilter[] = [] secondary: INestedFilter[] = []
): Map<number, AnnotationWrapper[]> { ): Map<number, AnnotationWrapper[]> {
if (!primary) { if (!primary) {
return; return;

View File

@ -1,57 +1,57 @@
<ng-container *ngIf="!filter.icon"> <ng-container *ngIf="!filter.icon">
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'redaction'" *ngIf="filter.id === 'redaction'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="R" label="R"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'recommendation'" *ngIf="filter.id === 'recommendation'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="R" label="R"
type="hexagon" type="hexagon"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.key === 'hint'" [color]="dictionaryColor" label="H" type="circle"></redaction-annotation-icon> <redaction-annotation-icon *ngIf="filter.id === 'hint'" [color]="dictionaryColor" label="H" type="circle"></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'manual-redaction'" *ngIf="filter.id === 'manual-redaction'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="M" label="M"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'skipped'" *ngIf="filter.id === 'skipped'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="S" label="S"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="isSuggestion(filter.key)" *ngIf="isSuggestion(filter.id)"
[color]="dictionaryColor" [color]="dictionaryColor"
label="S" label="S"
type="rhombus" type="rhombus"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="needsAnalysis(filter.key)" *ngIf="needsAnalysis(filter.id)"
[color]="dictionaryColor" [color]="dictionaryColor"
label="A" label="A"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'declined-suggestion'" *ngIf="filter.id === 'declined-suggestion'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="S" label="S"
type="rhombus" type="rhombus"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.key === 'none'" color="transparent" label="-" type="none"></redaction-annotation-icon> <redaction-annotation-icon *ngIf="filter.id === 'none'" color="transparent" label="-" type="none"></redaction-annotation-icon>
<redaction-annotation-icon <redaction-annotation-icon
*ngIf="filter.key === 'updated'" *ngIf="filter.id === 'updated'"
[color]="dictionaryColor" [color]="dictionaryColor"
label="U" label="U"
type="square" type="square"
></redaction-annotation-icon> ></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.key === 'image'" [color]="dictionaryColor" label="I" type="square"></redaction-annotation-icon> <redaction-annotation-icon *ngIf="filter.id === 'image'" [color]="dictionaryColor" label="I" type="square"></redaction-annotation-icon>
<div *ngIf="filter.key === 'comment'"> <div *ngIf="filter.id === 'comment'">
<mat-icon svgIcon="red:comment"></mat-icon> <mat-icon svgIcon="red:comment"></mat-icon>
</div> </div>
</ng-container> </ng-container>

View File

@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { NestedFilter } from '@iqser/common-ui'; import { INestedFilter } from '@iqser/common-ui';
@Component({ @Component({
selector: 'redaction-type-filter', selector: 'redaction-type-filter',
@ -8,7 +8,7 @@ import { NestedFilter } from '@iqser/common-ui';
styleUrls: ['./type-filter.component.scss'] styleUrls: ['./type-filter.component.scss']
}) })
export class TypeFilterComponent implements OnInit { export class TypeFilterComponent implements OnInit {
@Input() filter: NestedFilter; @Input() filter: INestedFilter;
dictionaryColor: string; dictionaryColor: string;
@ -36,6 +36,6 @@ export class TypeFilterComponent implements OnInit {
needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key); needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key);
ngOnInit(): void { ngOnInit(): void {
this.dictionaryColor = this._appStateService.getDictionaryColor(this.filter.key); this.dictionaryColor = this._appStateService.getDictionaryColor(this.filter.id);
} }
} }

View File

@ -1,6 +1,6 @@
import { Component, EventEmitter, forwardRef, Injector, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; import { Component, EventEmitter, forwardRef, Injector, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core';
import { EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { Dossier } from '../../../../../state/model/dossier'; import { Dossier } from '@state/model/dossier';
import { import {
CircleButtonTypes, CircleButtonTypes,
DefaultListingServices, DefaultListingServices,
@ -49,7 +49,6 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>; @ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
@ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef<never>; @ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef<never>;
@ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>; @ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>;
protected readonly _primaryKey = 'fileId';
constructor( constructor(
protected readonly _injector: Injector, protected readonly _injector: Injector,
@ -167,6 +166,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
id: file.fileId, id: file.fileId,
...file, ...file,
restoreDate, restoreDate,
searchKey: file.filename,
canRestore: this._canRestoreFile(restoreDate) canRestore: this._canRestoreFile(restoreDate)
}; };
} }

View File

@ -39,7 +39,7 @@
<div class="small-label stats-subtitle mb-6"> <div class="small-label stats-subtitle mb-6">
<div> <div>
<mat-icon svgIcon="red:template"></mat-icon> <mat-icon svgIcon="red:template"></mat-icon>
{{ dossier.dossierTemplateName }} {{ getDossierTemplateNameFor(dossier.dossierTemplateId) }}
</div> </div>
</div> </div>
<div class="small-label stats-subtitle"> <div class="small-label stats-subtitle">
@ -53,7 +53,7 @@
</div> </div>
<div> <div>
<mat-icon svgIcon="red:user"></mat-icon> <mat-icon svgIcon="red:user"></mat-icon>
{{ dossier.memberCount }} {{ dossier.memberIds.length }}
</div> </div>
<div> <div>
<mat-icon svgIcon="red:calendar"></mat-icon> <mat-icon svgIcon="red:calendar"></mat-icon>

View File

@ -16,7 +16,15 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { UserPreferenceService } from '@services/user-preference.service'; import { UserPreferenceService } from '@services/user-preference.service';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model'; import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { DefaultListingServices, keyChecker, ListingComponent, NestedFilter, TableColumnConfig, TableComponent } from '@iqser/common-ui'; import {
DefaultListingServices,
INestedFilter,
keyChecker,
ListingComponent,
NestedFilter,
TableColumnConfig,
TableComponent
} from '@iqser/common-ui';
import { workloadTranslations } from '../../translations/workload-translations'; import { workloadTranslations } from '../../translations/workload-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { fileStatusTranslations } from '../../translations/file-status-translations';
@ -46,11 +54,10 @@ export class DossierListingScreenComponent
tableColumnConfigs: TableColumnConfig<Dossier>[]; tableColumnConfigs: TableColumnConfig<Dossier>[];
dossiersChartData: DoughnutChartConfig[] = []; dossiersChartData: DoughnutChartConfig[] = [];
documentsChartData: DoughnutChartConfig[] = []; documentsChartData: DoughnutChartConfig[] = [];
@ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<never>; @ViewChild('nameTemplate', { static: true }) nameTemplate: TemplateRef<unknown>;
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<never>; @ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<unknown>;
@ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef<never>; @ViewChild('ownerTemplate', { static: true }) ownerTemplate: TemplateRef<unknown>;
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>; @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<unknown>;
protected readonly _primaryKey = 'dossierName';
private _lastScrolledIndex: number; private _lastScrolledIndex: number;
@ViewChild('needsWorkFilterTemplate', { @ViewChild('needsWorkFilterTemplate', {
read: TemplateRef, read: TemplateRef,
@ -85,6 +92,10 @@ export class DossierListingScreenComponent
routerLinkFn = (dossier: Dossier) => ['/main/dossiers/' + dossier.id]; routerLinkFn = (dossier: Dossier) => ['/main/dossiers/' + dossier.id];
getDossierTemplateNameFor(dossierTemplateId: string): string {
return this._appStateService.getDossierTemplateById(dossierTemplateId).name;
}
ngOnInit(): void { ngOnInit(): void {
this._configureTableColumns(); this._configureTableColumns();
this.calculateData(); this.calculateData();
@ -156,7 +167,7 @@ export class DossierListingScreenComponent
this.tableColumnConfigs = [ this.tableColumnConfigs = [
{ {
label: _('dossier-listing.table-col-names.name'), label: _('dossier-listing.table-col-names.name'),
sortByKey: 'dossierName', sortByKey: 'searchKey',
template: this.nameTemplate, template: this.nameTemplate,
width: '2fr' width: '2fr'
}, },
@ -214,23 +225,29 @@ export class DossierListingScreenComponent
allDistinctDossierTemplates.add(entry.dossierTemplateId); allDistinctDossierTemplates.add(entry.dossierTemplateId);
}); });
const statusFilters = [...allDistinctFileStatus].map<NestedFilter>(status => ({ const statusFilters = [...allDistinctFileStatus].map<INestedFilter>(
key: status, status =>
label: this._translateService.instant(fileStatusTranslations[status]) new NestedFilter({
})); id: status,
label: this._translateService.instant(fileStatusTranslations[status])
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'statusFilters', slug: 'statusFilters',
label: this._translateService.instant('filters.status'), label: this._translateService.instant('filters.status'),
icon: 'red:status', icon: 'red:status',
filters: statusFilters.sort(StatusSorter.byStatus), filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
checker: dossierStatusChecker checker: dossierStatusChecker
}); });
const peopleFilters = [...allDistinctPeople].map<NestedFilter>(userId => ({ const peopleFilters = [...allDistinctPeople].map<INestedFilter>(
key: userId, userId =>
label: this._userService.getNameForId(userId) new NestedFilter({
})); id: userId,
label: this._userService.getNameForId(userId)
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'peopleFilters', slug: 'peopleFilters',
@ -240,25 +257,31 @@ export class DossierListingScreenComponent
checker: dossierMemberChecker checker: dossierMemberChecker
}); });
const needsWorkFilters = [...allDistinctNeedsWork].map<NestedFilter>(type => ({ const needsWorkFilters = [...allDistinctNeedsWork].map<INestedFilter>(
key: type, type =>
label: workloadTranslations[type] new NestedFilter({
})); id: type,
label: workloadTranslations[type]
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'needsWorkFilters', slug: 'needsWorkFilters',
label: this._translateService.instant('filters.needs-work'), label: this._translateService.instant('filters.needs-work'),
icon: 'red:needs-work', icon: 'red:needs-work',
filterTemplate: this._needsWorkFilterTemplate, filterTemplate: this._needsWorkFilterTemplate,
filters: needsWorkFilters.sort(RedactionFilterSorter.byKey), filters: needsWorkFilters.sort((a, b) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]),
checker: annotationFilterChecker, checker: annotationFilterChecker,
matchAll: true matchAll: true
}); });
const dossierTemplateFilters = [...allDistinctDossierTemplates].map<NestedFilter>(id => ({ const dossierTemplateFilters = [...allDistinctDossierTemplates].map<INestedFilter>(
key: id, id =>
label: this._appStateService.getDossierTemplateById(id).name new NestedFilter({
})); id: id,
label: this._appStateService.getDossierTemplateById(id).name
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'dossierTemplateFilters', slug: 'dossierTemplateFilters',
@ -276,10 +299,13 @@ export class DossierListingScreenComponent
checker: (dw: Dossier) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false) checker: (dw: Dossier) => quickFilters.reduce((acc, f) => acc || (f.checked && f.checker(dw)), false)
}); });
const dossierFilters = this.entitiesService.all.map<NestedFilter>(dossier => ({ const dossierFilters = this.entitiesService.all.map<INestedFilter>(
key: dossier.dossierName, dossier =>
label: dossier.dossierName new NestedFilter({
})); id: dossier.dossierName,
label: dossier.dossierName
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'dossierNameFilter', slug: 'dossierNameFilter',
label: this._translateService.instant('dossier-listing.filters.label'), label: this._translateService.instant('dossier-listing.filters.label'),
@ -290,30 +316,30 @@ export class DossierListingScreenComponent
}); });
} }
private _createQuickFilters() { private _createQuickFilters(): INestedFilter[] {
const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers'); const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers');
const filters: NestedFilter[] = [ const filters: INestedFilter[] = [
{ {
key: 'my-dossiers', id: 'my-dossiers',
label: myDossiersLabel, label: myDossiersLabel,
checker: (dw: Dossier) => dw.ownerId === this.currentUser.id checker: (dw: Dossier) => dw.ownerId === this.currentUser.id
}, },
{ {
key: 'to-approve', id: 'to-approve',
label: this._translateService.instant('dossier-listing.quick-filters.to-approve'), label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
checker: (dw: Dossier) => dw.approverIds.includes(this.currentUser.id) checker: (dw: Dossier) => dw.approverIds.includes(this.currentUser.id)
}, },
{ {
key: 'to-review', id: 'to-review',
label: this._translateService.instant('dossier-listing.quick-filters.to-review'), label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
checker: (dw: Dossier) => dw.memberIds.includes(this.currentUser.id) checker: (dw: Dossier) => dw.memberIds.includes(this.currentUser.id)
}, },
{ {
key: 'other', id: 'other',
label: this._translateService.instant('dossier-listing.quick-filters.other'), label: this._translateService.instant('dossier-listing.quick-filters.other'),
checker: (dw: Dossier) => !dw.memberIds.includes(this.currentUser.id) checker: (dw: Dossier) => !dw.memberIds.includes(this.currentUser.id)
} }
]; ].map(filter => new NestedFilter(filter));
return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled); return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled);
} }

View File

@ -33,6 +33,7 @@ import { ActionConfig } from '@shared/components/page-header/models/action-confi
import { import {
CircleButtonTypes, CircleButtonTypes,
DefaultListingServices, DefaultListingServices,
INestedFilter,
keyChecker, keyChecker,
ListingComponent, ListingComponent,
ListingModes, ListingModes,
@ -45,7 +46,6 @@ import {
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service'; import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service';
import { DossierAttributeWithValue } from '@models/dossier-attributes.model'; import { DossierAttributeWithValue } from '@models/dossier-attributes.model';
import { UserPreferenceService } from '@services/user-preference.service';
import { workloadTranslations } from '../../translations/workload-translations'; import { workloadTranslations } from '../../translations/workload-translations';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { fileStatusTranslations } from '../../translations/file-status-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -80,15 +80,14 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
collapsedDetails = false; collapsedDetails = false;
dossierAttributes: DossierAttributeWithValue[] = []; dossierAttributes: DossierAttributeWithValue[] = [];
fileAttributeConfigs: FileAttributeConfig[]; fileAttributeConfigs: FileAttributeConfig[];
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<never>; @ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<unknown>;
@ViewChild('addedOnTemplate', { static: true }) addedOnTemplate: TemplateRef<never>; @ViewChild('addedOnTemplate', { static: true }) addedOnTemplate: TemplateRef<unknown>;
@ViewChild('attributeTemplate', { static: true }) attributeTemplate: TemplateRef<never>; @ViewChild('attributeTemplate', { static: true }) attributeTemplate: TemplateRef<unknown>;
@ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<never>; @ViewChild('needsWorkTemplate', { static: true }) needsWorkTemplate: TemplateRef<unknown>;
@ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<never>; @ViewChild('reviewerTemplate', { static: true }) reviewerTemplate: TemplateRef<unknown>;
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>; @ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<unknown>;
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>; @ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<unknown>;
readonly workflowConfig: WorkflowConfig<FileStatusWrapper, StatusEnum>; readonly workflowConfig: WorkflowConfig<FileStatusWrapper, StatusEnum>;
protected readonly _primaryKey = 'filename';
@ViewChild(DossierDetailsComponent, { static: false }) @ViewChild(DossierDetailsComponent, { static: false })
private readonly _dossierDetailsComponent: DossierDetailsComponent; private readonly _dossierDetailsComponent: DossierDetailsComponent;
private _lastScrolledIndex: number; private _lastScrolledIndex: number;
@ -112,7 +111,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _fileUploadService: FileUploadService, private readonly _fileUploadService: FileUploadService,
private readonly _statusOverlayService: StatusOverlayService, private readonly _statusOverlayService: StatusOverlayService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _fileDropOverlayService: FileDropOverlayService, private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _dossierAttributesService: DossierAttributesService, private readonly _dossierAttributesService: DossierAttributesService,
private readonly _fileActionService: FileActionService private readonly _fileActionService: FileActionService
@ -243,8 +241,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
.pipe(tap(index => (this._lastScrolledIndex = index))) .pipe(tap(index => (this._lastScrolledIndex = index)))
.subscribe(); .subscribe();
this.searchService.setSearchKey('filename');
this.dossierAttributes = await this._dossierAttributesService.getValues(this.currentDossier); this.dossierAttributes = await this._dossierAttributesService.getValues(this.currentDossier);
} catch (e) { } catch (e) {
} finally { } finally {
@ -296,22 +292,22 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
} }
@HostListener('drop', ['$event']) @HostListener('drop', ['$event'])
onDrop(event: DragEvent) { onDrop(event: DragEvent): void {
handleFileDrop(event, this.currentDossier, this._uploadFiles.bind(this)); handleFileDrop(event, this.currentDossier, this._uploadFiles.bind(this));
} }
@HostListener('dragover', ['$event']) @HostListener('dragover', ['$event'])
onDragOver(event) { onDragOver(event): void {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
} }
async uploadFiles(files: File[] | FileList) { async uploadFiles(files: File[] | FileList): Promise<void> {
await this._uploadFiles(convertFiles(files, this.currentDossier)); await this._uploadFiles(convertFiles(files, this.currentDossier));
this._fileInput.nativeElement.value = null; this._fileInput.nativeElement.value = null;
} }
async bulkActionPerformed() { async bulkActionPerformed(): Promise<void> {
this.entitiesService.setSelected([]); this.entitiesService.setSelected([]);
await this.reloadDossiers(); await this.reloadDossiers();
} }
@ -351,7 +347,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
this.tableColumnConfigs = [ this.tableColumnConfigs = [
{ {
label: _('dossier-overview.table-col-names.name'), label: _('dossier-overview.table-col-names.name'),
sortByKey: 'filename', sortByKey: 'searchKey',
template: this.filenameTemplate, template: this.filenameTemplate,
width: '3fr' width: '3fr'
}, },
@ -462,33 +458,40 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
}); });
}); });
const statusFilters = [...allDistinctFileStatusWrapper].map<NestedFilter>(item => ({ const statusFilters = [...allDistinctFileStatusWrapper].map<INestedFilter>(
key: item, item =>
label: this._translateService.instant(fileStatusTranslations[item]) new NestedFilter({
})); id: item,
label: this._translateService.instant(fileStatusTranslations[item])
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'statusFilters', slug: 'statusFilters',
label: this._translateService.instant('filters.status'), label: this._translateService.instant('filters.status'),
icon: 'red:status', icon: 'red:status',
filters: statusFilters.sort(StatusSorter.byStatus), filters: statusFilters.sort((a, b) => StatusSorter[a.id] - StatusSorter[b.id]),
checker: keyChecker('status') checker: keyChecker('status')
}); });
const peopleFilters = []; const peopleFilters: INestedFilter[] = [];
if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) { if (allDistinctPeople.has(undefined) || allDistinctPeople.has(null)) {
allDistinctPeople.delete(undefined); allDistinctPeople.delete(undefined);
allDistinctPeople.delete(null); allDistinctPeople.delete(null);
peopleFilters.push({ peopleFilters.push(
key: null, new NestedFilter({
label: this._translateService.instant('initials-avatar.unassigned') id: null,
}); label: this._translateService.instant('initials-avatar.unassigned')
})
);
} }
allDistinctPeople.forEach(userId => { allDistinctPeople.forEach(userId => {
peopleFilters.push({ peopleFilters.push(
key: userId, new NestedFilter({
label: this._userService.getNameForId(userId) id: userId,
}); label: this._userService.getNameForId(userId)
})
);
}); });
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'peopleFilters', slug: 'peopleFilters',
@ -498,10 +501,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
checker: keyChecker('currentReviewer') checker: keyChecker('currentReviewer')
}); });
const needsWorkFilters = [...allDistinctNeedsWork].map<NestedFilter>(item => ({ const needsWorkFilters = [...allDistinctNeedsWork].map<INestedFilter>(
key: item, item =>
label: workloadTranslations[item] new NestedFilter({
})); id: item,
label: workloadTranslations[item]
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'needsWorkFilters', slug: 'needsWorkFilters',
@ -520,11 +526,14 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
slug: key, slug: key,
label: key, label: key,
icon: 'red:template', icon: 'red:template',
filters: [...filterValue].map<NestedFilter>((value: string) => ({ filters: [...filterValue].map<INestedFilter>(
key: value, (value: string) =>
label: value === '-' ? this._translateService.instant('filters.empty') : value new NestedFilter({
})), id: value,
checker: (input: FileStatusWrapper, filter: NestedFilter) => filter.key === input.fileAttributes.attributeIdToValue[id] label: value === '-' ? this._translateService.instant('filters.empty') : value
})
),
checker: (input: FileStatusWrapper, filter: INestedFilter) => filter.id === input.fileAttributes.attributeIdToValue[id]
}); });
}); });
@ -537,10 +546,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
this.checkedNotRequiredFilters.reduce((acc, f) => acc || f.checker(file), false)) this.checkedNotRequiredFilters.reduce((acc, f) => acc || f.checker(file), false))
}); });
const filesNamesFilters = this.entitiesService.all.map<NestedFilter>(file => ({ const filesNamesFilters = this.entitiesService.all.map<INestedFilter>(
key: file.filename, file =>
label: file.filename new NestedFilter({
})); id: file.filename,
label: file.filename
})
);
this.filterService.addFilterGroup({ this.filterService.addFilterGroup({
slug: 'filesNamesFilter', slug: 'filesNamesFilter',
@ -552,13 +564,13 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
}); });
} }
private _createQuickFilters() { private _createQuickFilters(): INestedFilter[] {
let quickFilters = []; let quickFilters: INestedFilter[] = [];
if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) { if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._configService.values.RECENT_PERIOD_IN_HOURS; const recentPeriod = this._configService.values.RECENT_PERIOD_IN_HOURS;
quickFilters = [ quickFilters = [
{ {
key: 'recent', id: 'recent',
label: this._translateService.instant('dossier-overview.quick-filters.recent', { label: this._translateService.instant('dossier-overview.quick-filters.recent', {
hours: recentPeriod hours: recentPeriod
}), }),
@ -571,20 +583,20 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
return [ return [
...quickFilters, ...quickFilters,
{ {
key: 'assigned-to-me', id: 'assigned-to-me',
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'), label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
checker: (file: FileStatusWrapper) => file.currentReviewer === this.currentUser.id checker: (file: FileStatusWrapper) => file.currentReviewer === this.currentUser.id
}, },
{ {
key: 'unassigned', id: 'unassigned',
label: this._translateService.instant('dossier-overview.quick-filters.unassigned'), label: this._translateService.instant('dossier-overview.quick-filters.unassigned'),
checker: (file: FileStatusWrapper) => !file.currentReviewer checker: (file: FileStatusWrapper) => !file.currentReviewer
}, },
{ {
key: 'assigned-to-others', id: 'assigned-to-others',
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'), label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id checker: (file: FileStatusWrapper) => !!file.currentReviewer && file.currentReviewer !== this.currentUser.id
} }
]; ].map(filter => new NestedFilter(filter));
} }
} }

View File

@ -8,8 +8,8 @@ import {
CircleButtonTypes, CircleButtonTypes,
Debounce, Debounce,
FilterService, FilterService,
INestedFilter,
LoadingService, LoadingService,
NestedFilter,
processFilters, processFilters,
Toaster Toaster
} from '@iqser/common-ui'; } from '@iqser/common-ui';
@ -80,7 +80,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
read: TemplateRef, read: TemplateRef,
static: true static: true
}) })
private readonly _filterTemplate: TemplateRef<NestedFilter>; private readonly _filterTemplate: TemplateRef<INestedFilter>;
@ViewChild('fileActions') fileActions: FileActionsComponent; @ViewChild('fileActions') fileActions: FileActionsComponent;
constructor( constructor(

View File

@ -1,23 +1,29 @@
import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { DefaultListingServices, IListable, keyChecker, ListingComponent, LoadingService, TableColumnConfig } from '@iqser/common-ui'; import {
import { MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http'; DefaultListingServices,
IListable,
keyChecker,
ListingComponent,
LoadingService,
NestedFilter,
TableColumnConfig
} from '@iqser/common-ui';
import { List, MatchedDocument, SearchControllerService, SearchResult } from '@redaction/red-ui-http';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators'; import { debounceTime, map, skip, switchMap, tap } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { fileStatusTranslations } from '../../translations/file-status-translations'; import { fileStatusTranslations } from '../../translations/file-status-translations';
import { SearchPositions } from '@shared/components/page-header/models/search-positions.type'; import { SearchPositions } from '@shared/components/page-header/models/search-positions.type';
import { Dossier } from '../../../../state/model/dossier';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { RouterHistoryService } from '@services/router-history.service'; import { RouterHistoryService } from '@services/router-history.service';
interface ListItem extends IListable { interface ListItem extends IListable {
readonly dossierId: string; readonly dossierId: string;
readonly filename: string; readonly filename: string;
readonly unmatched: readonly string[] | null; readonly unmatched: List | null;
readonly highlights: Record<string, readonly string[]>; readonly highlights: Record<string, List>;
readonly routerLink: string; readonly routerLink: string;
readonly status: string; readonly status: string;
readonly dossierName: string; readonly dossierName: string;
@ -26,7 +32,7 @@ interface ListItem extends IListable {
interface SearchInput { interface SearchInput {
readonly query: string; readonly query: string;
readonly dossierIds?: readonly string[]; readonly dossierIds?: List;
} }
@Component({ @Component({
@ -53,7 +59,6 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
tap(result => this.entitiesService.setEntities(result)), tap(result => this.entitiesService.setEntities(result)),
tap(() => this._loadingService.stop()) tap(() => this._loadingService.stop())
); );
protected readonly _primaryKey = 'filename';
constructor( constructor(
private readonly _router: Router, private readonly _router: Router,
@ -72,10 +77,13 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
label: this._translateService.instant('search-screen.filters.by-dossier'), label: this._translateService.instant('search-screen.filters.by-dossier'),
filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'), filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'),
icon: 'red:folder', icon: 'red:folder',
filters: this._appStateService.allDossiers.map(dossier => ({ filters: this._appStateService.allDossiers.map(
key: dossier.id, dossier =>
label: dossier.dossierName new NestedFilter({
})), id: dossier.id,
label: dossier.dossierName
})
),
checker: keyChecker('id') checker: keyChecker('id')
}); });
@ -89,17 +97,13 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
this.addSubscription = this.searchService.valueChanges$.pipe(debounceTime(300)).subscribe(value => this.updateNavigation(value)); this.addSubscription = this.searchService.valueChanges$.pipe(debounceTime(300)).subscribe(value => this.updateNavigation(value));
this.addSubscription = this.filterService.filterGroups$.pipe(skip(1)).subscribe(group => { this.addSubscription = this.filterService.filterGroups$.pipe(skip(1)).subscribe(group => {
const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.key); const dossierIds = group[0].filters.filter(v => v.checked).map(v => v.id);
this.search$.next({ query: this.searchService.searchValue, dossierIds: dossierIds }); this.search$.next({ query: this.searchService.searchValue, dossierIds: dossierIds });
}); });
} }
routerLinkFn = (entity: ListItem) => [entity.routerLink]; routerLinkFn = (entity: ListItem) => [entity.routerLink];
setInitialConfig(): void {
return;
}
updateNavigation(query: string, mustContain?: string): void { updateNavigation(query: string, mustContain?: string): void {
const newQuery = query?.replace(mustContain, `"${mustContain}"`); const newQuery = query?.replace(mustContain, `"${mustContain}"`);
const queryParams = newQuery && newQuery !== '' ? { query: newQuery } : {}; const queryParams = newQuery && newQuery !== '' ? { query: newQuery } : {};
@ -137,14 +141,6 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
this.search$.next({ query, dossierIds: dossierId ? [dossierId] : [] }); this.search$.next({ query, dossierIds: dossierId ? [dossierId] : [] });
} }
private _getFileWrapper(dossierId: string, fileId: string): FileStatusWrapper {
return this._appStateService.getFileById(dossierId, fileId);
}
private _getDossierWrapper(dossierId: string): Dossier {
return this._appStateService.getDossierById(dossierId);
}
private _toMatchedDocuments({ matchedDocuments }: SearchResult): MatchedDocument[] { private _toMatchedDocuments({ matchedDocuments }: SearchResult): MatchedDocument[] {
return matchedDocuments.filter(doc => doc.score > 0 && doc.matchedTerms.length > 0); return matchedDocuments.filter(doc => doc.score > 0 && doc.matchedTerms.length > 0);
} }
@ -154,7 +150,7 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
} }
private _toListItem({ dossierId, fileId, unmatchedTerms, highlights }: MatchedDocument): ListItem { private _toListItem({ dossierId, fileId, unmatchedTerms, highlights }: MatchedDocument): ListItem {
const fileWrapper = this._getFileWrapper(dossierId, fileId); const fileWrapper = this._appStateService.getFileById(dossierId, fileId);
if (!fileWrapper) { if (!fileWrapper) {
return undefined; return undefined;
} }
@ -166,8 +162,9 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
highlights, highlights,
status: fileWrapper.status, status: fileWrapper.status,
numberOfPages: fileWrapper.numberOfPages, numberOfPages: fileWrapper.numberOfPages,
dossierName: this._getDossierWrapper(dossierId).dossierName, dossierName: this._appStateService.getDossierById(dossierId).dossierName,
filename: fileWrapper.filename, filename: fileWrapper.filename,
searchKey: fileWrapper.filename,
routerLink: `/main/dossiers/${dossierId}/file/${fileId}` routerLink: `/main/dossiers/${dossierId}/file/${fileId}`
}; };
} }

View File

@ -1,38 +1,36 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { SuperTypeSorter } from '@utils/sorters/super-type-sorter'; import { SuperTypeSorter } from '@utils/sorters/super-type-sorter';
import { handleCheckedValue, NestedFilter } from '@iqser/common-ui'; import { handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui';
import { annotationTypesTranslations } from '../../../translations/annotation-types-translations'; import { annotationTypesTranslations } from '../../../translations/annotation-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Injectable() @Injectable()
export class AnnotationProcessingService { export class AnnotationProcessingService {
static get secondaryAnnotationFilters(): NestedFilter[] { static get secondaryAnnotationFilters(): INestedFilter[] {
return [ return [
{ {
key: 'with-comments', id: 'with-comments',
icon: 'red:comment', icon: 'red:comment',
label: _('filter-menu.with-comments'), label: _('filter-menu.with-comments'),
checked: false, checked: false,
topLevelFilter: true, topLevelFilter: true,
children: [],
checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0 checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0
}, },
{ {
key: 'with-reason-changes', id: 'with-reason-changes',
icon: 'red:reason', icon: 'red:reason',
label: _('filter-menu.with-reason-changes'), label: _('filter-menu.with-reason-changes'),
checked: false, checked: false,
topLevelFilter: true, topLevelFilter: true,
children: [],
checker: (annotation: AnnotationWrapper) => annotation?.legalBasisChangeValue?.length > 0 checker: (annotation: AnnotationWrapper) => annotation?.legalBasisChangeValue?.length > 0
} }
]; ].map(item => new NestedFilter(item));
} }
getAnnotationFilter(annotations: AnnotationWrapper[]): NestedFilter[] { getAnnotationFilter(annotations: AnnotationWrapper[]): INestedFilter[] {
const filterMap = new Map<string, NestedFilter>(); const filterMap = new Map<string, INestedFilter>();
const filters: NestedFilter[] = []; const filters: INestedFilter[] = [];
annotations?.forEach(a => { annotations?.forEach(a => {
const topLevelFilter = a.superType !== 'hint' && a.superType !== 'redaction' && a.superType !== 'recommendation'; const topLevelFilter = a.superType !== 'hint' && a.superType !== 'redaction' && a.superType !== 'recommendation';
@ -49,11 +47,11 @@ export class AnnotationProcessingService {
if (!parentFilter) { if (!parentFilter) {
parentFilter = this._createParentFilter(a.superType, filterMap, filters); parentFilter = this._createParentFilter(a.superType, filterMap, filters);
} }
const childFilter = { const childFilter: IFilter = {
key: a.type, id: a.type,
label: a.type, label: a.type,
searchKey: a.type,
checked: false, checked: false,
filters: [],
matches: 1 matches: 1
}; };
filterMap.set(key, childFilter); filterMap.set(key, childFilter);
@ -63,7 +61,7 @@ export class AnnotationProcessingService {
}); });
for (const filter of filters) { for (const filter of filters) {
filter.children.sort((a, b) => a.key.localeCompare(b.key)); filter.children.sort((a, b) => a.id.localeCompare(b.id));
handleCheckedValue(filter); handleCheckedValue(filter);
if (filter.checked || filter.indeterminate) { if (filter.checked || filter.indeterminate) {
filter.expanded = true; filter.expanded = true;
@ -73,13 +71,13 @@ export class AnnotationProcessingService {
} }
} }
return filters.sort((a, b) => SuperTypeSorter[a.key] - SuperTypeSorter[b.key]); return filters.sort((a, b) => SuperTypeSorter[a.id] - SuperTypeSorter[b.id]);
} }
filterAndGroupAnnotations( filterAndGroupAnnotations(
annotations: AnnotationWrapper[], annotations: AnnotationWrapper[],
primaryFilters: NestedFilter[], primaryFilters: INestedFilter[],
secondaryFilters?: NestedFilter[] secondaryFilters?: INestedFilter[]
): Map<number, AnnotationWrapper[]> { ): Map<number, AnnotationWrapper[]> {
const obj = new Map<number, AnnotationWrapper[]>(); const obj = new Map<number, AnnotationWrapper[]>();
@ -116,21 +114,20 @@ export class AnnotationProcessingService {
return obj; return obj;
} }
private _createParentFilter(key: string, filterMap: Map<string, NestedFilter>, filters: NestedFilter[]) { private _createParentFilter(key: string, filterMap: Map<string, INestedFilter>, filters: INestedFilter[]) {
const filter: NestedFilter = { const filter: INestedFilter = new NestedFilter({
key: key, id: key,
topLevelFilter: true, topLevelFilter: true,
matches: 1, matches: 1,
label: annotationTypesTranslations[key], label: annotationTypesTranslations[key]
children: [] });
};
filterMap.set(key, filter); filterMap.set(key, filter);
filters.push(filter); filters.push(filter);
return filter; return filter;
} }
private _getFlatFilters(filters: NestedFilter[], filterBy?: (f: NestedFilter) => boolean) { private _getFlatFilters(filters: INestedFilter[], filterBy?: (f: INestedFilter) => boolean) {
const flatFilters: NestedFilter[] = []; const flatFilters: INestedFilter[] = [];
filters.forEach(filter => { filters.forEach(filter => {
flatFilters.push(filter); flatFilters.push(filter);
@ -140,7 +137,7 @@ export class AnnotationProcessingService {
return filterBy ? flatFilters.filter(f => filterBy(f)) : flatFilters; return filterBy ? flatFilters.filter(f => filterBy(f)) : flatFilters;
} }
private _matchesOne = (filters: NestedFilter[], condition: (filter: NestedFilter) => boolean): boolean => { private _matchesOne = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
if (filters.length === 0) { if (filters.length === 0) {
return true; return true;
} }
@ -154,7 +151,7 @@ export class AnnotationProcessingService {
return false; return false;
}; };
private _matchesAll = (filters: NestedFilter[], condition: (filter: NestedFilter) => boolean): boolean => { private _matchesAll = (filters: INestedFilter[], condition: (filter: INestedFilter) => boolean): boolean => {
if (filters.length === 0) { if (filters.length === 0) {
return true; return true;
} }
@ -168,11 +165,11 @@ export class AnnotationProcessingService {
return true; return true;
}; };
private _checkByFilterKey = (filter: NestedFilter, annotation: AnnotationWrapper) => { private _checkByFilterKey = (filter: INestedFilter, annotation: AnnotationWrapper) => {
const superType = annotation.superType; const superType = annotation.superType;
const isNotTopLevelFilter = superType === 'hint' || superType === 'redaction' || superType === 'recommendation'; const isNotTopLevelFilter = superType === 'hint' || superType === 'redaction' || superType === 'recommendation';
return filter.key === superType || (filter.key === annotation.type && isNotTopLevelFilter); return filter.id === superType || (filter.id === annotation.type && isNotTopLevelFilter);
}; };
private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] { private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {

View File

@ -1,7 +1,7 @@
import { Component, EventEmitter, Input, Optional, Output, TemplateRef } from '@angular/core'; import { Component, EventEmitter, Input, Optional, Output, TemplateRef } from '@angular/core';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model'; import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import { ButtonConfig } from '@shared/components/page-header/models/button-config.model'; import { ButtonConfig } from '@shared/components/page-header/models/button-config.model';
import { FilterService, IconButtonTypes, Listable, SearchService } from '@iqser/common-ui'; import { FilterService, IListable, SearchService } from '@iqser/common-ui';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { combineLatest, Observable, of } from 'rxjs'; import { combineLatest, Observable, of } from 'rxjs';
import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type'; import { SearchPosition, SearchPositions } from '@shared/components/page-header/models/search-positions.type';
@ -12,7 +12,7 @@ import { FileAttributeConfig } from '@redaction/red-ui-http';
templateUrl: './page-header.component.html', templateUrl: './page-header.component.html',
styleUrls: ['./page-header.component.scss'] styleUrls: ['./page-header.component.scss']
}) })
export class PageHeaderComponent<T extends Listable> { export class PageHeaderComponent<T extends IListable> {
readonly searchPositions = SearchPositions; readonly searchPositions = SearchPositions;
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;

View File

@ -56,7 +56,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.key === key)?.checked)); return this.statusFilters$.pipe(map(all => all?.find(e => e.id === key)?.checked));
} }
calculateChartData() { calculateChartData() {

View File

@ -1,38 +1,38 @@
import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { Dossier } from '../state/model/dossier'; import { Dossier } from '../state/model/dossier';
import { handleCheckedValue, NestedFilter } from '@iqser/common-ui'; import { handleCheckedValue, INestedFilter } from '@iqser/common-ui';
export function handleFilterDelta(oldFilters: NestedFilter[], newFilters: NestedFilter[], allFilters: NestedFilter[]) { export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) {
const newFiltersDelta = {}; const newFiltersDelta = {};
for (const newFilter of newFilters) { for (const newFilter of newFilters) {
const oldFilter = oldFilters.find(f => f.key === newFilter.key); const oldFilter = oldFilters.find(f => f.id === newFilter.id);
if (!oldFilter || oldFilter.matches !== newFilter.matches) { if (!oldFilter || oldFilter.matches !== newFilter.matches) {
newFiltersDelta[newFilter.key] = {}; newFiltersDelta[newFilter.id] = {};
newFilter.children.forEach(filter => (newFiltersDelta[newFilter.key][filter.key] = {})); newFilter.children.forEach(filter => (newFiltersDelta[newFilter.id][filter.id] = {}));
} }
if (!oldFilter) { if (!oldFilter) {
for (const childFilter of newFilter.children) { for (const childFilter of newFilter.children) {
const oldFilterChild = oldFilter?.children.find(f => f.key === childFilter.key); const oldFilterChild = oldFilter?.children.find(f => f.id === childFilter.id);
if (!oldFilterChild || oldFilterChild.matches !== childFilter.matches) { if (!oldFilterChild || oldFilterChild.matches !== childFilter.matches) {
if (!newFiltersDelta[newFilter.key]) { if (!newFiltersDelta[newFilter.id]) {
newFiltersDelta[newFilter.key] = {}; newFiltersDelta[newFilter.id] = {};
} }
newFiltersDelta[newFilter.key][childFilter.key] = {}; newFiltersDelta[newFilter.id][childFilter.id] = {};
} }
} }
} }
} }
for (const key of Object.keys(newFiltersDelta)) { for (const key of Object.keys(newFiltersDelta)) {
const foundFilter = allFilters.find(f => f.key === key); const foundFilter = allFilters.find(f => f.id === key);
if (foundFilter) { if (foundFilter) {
// if has children // if has children
if (!foundFilter.children?.length) { if (!foundFilter.children?.length) {
foundFilter.checked = true; foundFilter.checked = true;
} else { } else {
for (const subKey of Object.keys(newFiltersDelta[key])) { for (const subKey of Object.keys(newFiltersDelta[key])) {
const childFilter = foundFilter.children.find(f => f.key === subKey); const childFilter = foundFilter.children.find(f => f.id === subKey);
if (childFilter) { if (childFilter) {
childFilter.checked = true; childFilter.checked = true;
} }
@ -45,8 +45,8 @@ export function handleFilterDelta(oldFilters: NestedFilter[], newFilters: Nested
}); });
} }
export const annotationFilterChecker = (input: FileStatusWrapper | Dossier, filter: NestedFilter) => { export const annotationFilterChecker = (input: FileStatusWrapper | Dossier, filter: INestedFilter) => {
switch (filter.key) { switch (filter.id) {
case 'analysis': { case 'analysis': {
if (input instanceof Dossier) { if (input instanceof Dossier) {
return input.reanalysisRequired; return input.reanalysisRequired;
@ -78,10 +78,10 @@ export const annotationFilterChecker = (input: FileStatusWrapper | Dossier, filt
} }
}; };
export const dossierStatusChecker = (dw: Dossier, filter: NestedFilter) => dw.hasStatus(filter.key); export const dossierStatusChecker = (dw: Dossier, filter: INestedFilter) => dw.hasStatus(filter.id);
export const dossierMemberChecker = (dw: Dossier, filter: NestedFilter) => dw.hasMember(filter.key); export const dossierMemberChecker = (dw: Dossier, filter: INestedFilter) => dw.hasMember(filter.id);
export const dossierTemplateChecker = (dw: Dossier, filter: NestedFilter) => dw.dossierTemplateId === filter.key; export const dossierTemplateChecker = (dw: Dossier, filter: INestedFilter) => dw.dossierTemplateId === filter.id;
export const dossierApproverChecker = (dw: Dossier, filter: NestedFilter) => dw.approverIds.includes(filter.key); export const dossierApproverChecker = (dw: Dossier, filter: INestedFilter) => dw.approverIds.includes(filter.id);

View File

@ -6,5 +6,5 @@ export const RedactionFilterSorter = {
hint: 4, hint: 4,
suggestion: 5, suggestion: 5,
none: 6, none: 6,
byKey: (a: { key: string }, b: { key: string }) => RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key] byKey: (a: { id: string }, b: { id: string }) => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id]
}; };