Pull request #272: Common lib
Merge in RED/ui from common-lib to master * commit '7b375e481ab383d9847aed00d12490d6f2e9b891': fix dossier's status bar update lib eslint fix icons bg, filter padding, make filter popup reactive
This commit is contained in:
commit
0021a417e2
@ -4,7 +4,7 @@
|
||||
"plugins": ["@nrwl/nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@nrwl/nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
@ -36,15 +36,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"files": ["*.ts"],
|
||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
@ -79,7 +74,7 @@
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"warn",
|
||||
"error",
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ export class FileStatusWrapper implements FileStatus {
|
||||
readonly analysisDuration = this.fileStatus.analysisDuration;
|
||||
readonly analysisRequired = this.fileStatus.analysisRequired && !this.fileStatus.excluded;
|
||||
readonly approvalDate = this.fileStatus.approvalDate;
|
||||
currentReviewer = this.fileStatus.currentReviewer;
|
||||
readonly currentReviewer = this.fileStatus.currentReviewer;
|
||||
readonly dictionaryVersion = this.fileStatus.dictionaryVersion;
|
||||
readonly dossierDictionaryVersion = this.fileStatus.dossierDictionaryVersion;
|
||||
readonly dossierId = this.fileStatus.dossierId;
|
||||
@ -67,6 +67,7 @@ export class FileStatusWrapper implements FileStatus {
|
||||
readonly hintsOnly = this.hasHints && !this.hasRedactions;
|
||||
readonly hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
||||
|
||||
readonly isUnassigned = !this.currentReviewer
|
||||
readonly isError = this.status === FileStatus.StatusEnum.ERROR;
|
||||
readonly isProcessing = processingStatuses.includes(this.status);
|
||||
readonly isApproved = this.status === FileStatus.StatusEnum.APPROVED;
|
||||
@ -78,10 +79,6 @@ export class FileStatusWrapper implements FileStatus {
|
||||
readonly isWorkable = !this.isProcessing && this.canBeOpened;
|
||||
readonly canBeOCRed = !this.excluded && !this.lastOCRTime && ['UNASSIGNED', 'UNDER_REVIEW', 'UNDER_APPROVAL'].includes(this.status);
|
||||
|
||||
get isUnassigned() {
|
||||
return !this.currentReviewer;
|
||||
}
|
||||
|
||||
private get _pages() {
|
||||
if (this.fileStatus.status === 'ERROR') {
|
||||
return -1;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<iqser-status-bar [configs]="statusBarConfig"></iqser-status-bar>
|
||||
<iqser-status-bar [configs]="statusConfig"></iqser-status-bar>
|
||||
<div class="action-buttons">
|
||||
<iqser-circle-button
|
||||
(action)="openEditDossierDialog($event, dossier)"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { DossierWrapper } from '@state/model/dossier.wrapper';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
@ -6,7 +6,6 @@ import { AppStateService } from '@state/app-state.service';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { CircleButtonTypes, StatusBarConfig } from '@iqser/common-ui';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dossier-listing-actions',
|
||||
@ -14,15 +13,13 @@ import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
styleUrls: ['./dossier-listing-actions.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DossierListingActionsComponent implements OnInit {
|
||||
export class DossierListingActionsComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly currentUser = this._userService.currentUser;
|
||||
|
||||
@Input() dossier: DossierWrapper;
|
||||
@Output() actionPerformed = new EventEmitter<DossierWrapper | undefined>();
|
||||
|
||||
statusBarConfig: readonly StatusBarConfig<string>[];
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly appStateService: AppStateService,
|
||||
@ -30,26 +27,24 @@ export class DossierListingActionsComponent implements OnInit {
|
||||
private readonly _userService: UserService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.statusBarConfig = this._getStatusConfig(this.dossier.files);
|
||||
}
|
||||
|
||||
openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper) {
|
||||
openEditDossierDialog($event: MouseEvent, dossierWrapper: DossierWrapper): void {
|
||||
this._dialogService.openDialog('editDossier', $event, {
|
||||
dossierWrapper,
|
||||
afterSave: () => this.actionPerformed.emit()
|
||||
});
|
||||
}
|
||||
|
||||
reanalyseDossier($event: MouseEvent, dossier: DossierWrapper) {
|
||||
reanalyseDossier($event: MouseEvent, dossier: DossierWrapper): void {
|
||||
$event.stopPropagation();
|
||||
this.appStateService.reanalyzeDossier(dossier).then(() => {
|
||||
this.appStateService.loadAllDossiers().then(() => this.actionPerformed.emit());
|
||||
});
|
||||
}
|
||||
|
||||
private _getStatusConfig(files: readonly FileStatusWrapper[]) {
|
||||
const obj = files.reduce((acc, file) => {
|
||||
get statusConfig(): readonly StatusBarConfig<string>[] {
|
||||
if (!this.dossier) return [];
|
||||
|
||||
const obj = this.dossier.files.reduce((acc, file) => {
|
||||
const status = file.status;
|
||||
if (!acc[status]) {
|
||||
acc[status] = 1;
|
||||
|
||||
@ -7,11 +7,9 @@
|
||||
translate="file-preview.tabs.annotations.select"
|
||||
></div>
|
||||
<redaction-popup-filter
|
||||
(filtersChanged)="filtersChanged($event)"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[filterTemplate]="annotationFilterTemplate"
|
||||
[primaryFilters]="primaryFilters"
|
||||
[secondaryFilters]="secondaryFilters"
|
||||
[primaryFiltersSlug]="'primaryFilters'"
|
||||
[secondaryFiltersSlug]="'secondaryFilters'"
|
||||
></redaction-popup-filter>
|
||||
</div>
|
||||
</div>
|
||||
@ -233,14 +231,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #annotationFilterTemplate let-filter="filter">
|
||||
<redaction-type-filter *ngIf="_(filter).topLevelFilter" [filter]="filter"></redaction-type-filter>
|
||||
<ng-container *ngIf="!_(filter).topLevelFilter">
|
||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.key"></redaction-dictionary-annotation-icon>
|
||||
{{ filter.key | humanize: false }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
||||
<iqser-circle-button
|
||||
(action)="toggleSkipped.emit($event)"
|
||||
|
||||
@ -38,8 +38,6 @@ export class FileWorkloadComponent {
|
||||
@Input() activeViewerPage: number;
|
||||
@Input() shouldDeselectAnnotationsOnPageChange: boolean;
|
||||
@Input() dialogRef: MatDialogRef<any>;
|
||||
@Input() primaryFilters: NestedFilter[];
|
||||
@Input() secondaryFilters: NestedFilter[];
|
||||
@Input() fileData: FileDataModel;
|
||||
@Input() hideSkipped: boolean;
|
||||
@Input() excludePages: boolean;
|
||||
@ -267,10 +265,6 @@ export class FileWorkloadComponent {
|
||||
this.selectPage.emit(this._nextPageWithAnnotations());
|
||||
}
|
||||
|
||||
_(filter): NestedFilter {
|
||||
return filter as NestedFilter;
|
||||
}
|
||||
|
||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||
if (
|
||||
(!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
|
||||
|
||||
@ -23,54 +23,54 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" redactionHasScrollbar>
|
||||
<div
|
||||
*cdkVirtualFor="let item of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||
[class.pointer]="!!item"
|
||||
[routerLink]="[item.routerLink]"
|
||||
*cdkVirtualFor="let dossier of sortedDisplayedEntities$ | async; trackBy: trackByPrimaryKey"
|
||||
[class.pointer]="!!dossier"
|
||||
[routerLink]="['/main/dossiers/' + dossier.dossierId.toString()]"
|
||||
class="table-item"
|
||||
>
|
||||
<div class="filename">
|
||||
<div [matTooltip]="item.dossierName" class="table-item-title heading" matTooltipPosition="above">
|
||||
{{ item.dossierName }}
|
||||
<div [matTooltip]="dossier.dossierName" class="table-item-title heading" matTooltipPosition="above">
|
||||
{{ dossier.dossierName }}
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ item.dossierTemplateName }}
|
||||
{{ dossier.dossierTemplateName }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:document"></mat-icon>
|
||||
{{ item.dossier.filesLength }}
|
||||
{{ dossier.filesLength }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
{{ item.dossier.totalNumberOfPages }}
|
||||
{{ dossier.totalNumberOfPages }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
{{ item.dossier.memberCount }}
|
||||
{{ dossier.memberCount }}
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
{{ item.dossier.date | date: 'mediumDate' }}
|
||||
{{ dossier.date | date: 'mediumDate' }}
|
||||
</div>
|
||||
<div *ngIf="item.dossier.dueDate">
|
||||
<div *ngIf="dossier.dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
{{ item.dossier.dueDate | date: 'mediumDate' }}
|
||||
{{ dossier.dueDate | date: 'mediumDate' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<redaction-needs-work-badge [needsWorkInput]="item.dossier"></redaction-needs-work-badge>
|
||||
<redaction-needs-work-badge [needsWorkInput]="dossier"></redaction-needs-work-badge>
|
||||
</div>
|
||||
<div class="user-column">
|
||||
<redaction-initials-avatar [userId]="item.dossier.ownerId" [withName]="true"></redaction-initials-avatar>
|
||||
<redaction-initials-avatar [userId]="dossier.ownerId" [withName]="true"></redaction-initials-avatar>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<redaction-dossier-listing-actions
|
||||
(actionPerformed)="calculateData()"
|
||||
[dossier]="item.dossier"
|
||||
[dossier]="dossier"
|
||||
></redaction-dossier-listing-actions>
|
||||
</div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { AfterViewInit, Component, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { Dossier, DossierTemplateModel } from '@redaction/red-ui-http';
|
||||
import { Dossier } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
@ -29,20 +29,13 @@ import {
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
|
||||
interface ListItem {
|
||||
readonly dossierName: string;
|
||||
readonly routerLink: string;
|
||||
readonly dossierTemplateName: string;
|
||||
readonly dossier: DossierWrapper;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './dossier-listing-screen.component.html',
|
||||
styleUrls: ['./dossier-listing-screen.component.scss'],
|
||||
providers: [...DefaultListingServices]
|
||||
})
|
||||
export class DossierListingScreenComponent
|
||||
extends ListingComponent<ListItem>
|
||||
extends ListingComponent<DossierWrapper>
|
||||
implements OnInit, AfterViewInit, OnDestroy, OnAttach, OnDetach
|
||||
{
|
||||
readonly itemSize = 85;
|
||||
@ -52,13 +45,13 @@ export class DossierListingScreenComponent
|
||||
readonly buttonConfigs: readonly ButtonConfig[] = [
|
||||
{
|
||||
label: _('dossier-listing.add-new'),
|
||||
action: () => this.openAddDossierDialog(),
|
||||
action: (): void => this.openAddDossierDialog(),
|
||||
hide: !this.currentUser.isManager,
|
||||
icon: 'red:plus',
|
||||
type: 'primary'
|
||||
}
|
||||
];
|
||||
readonly tableColumnConfigs: readonly TableColumnConfig<ListItem>[] = [
|
||||
readonly tableColumnConfigs: readonly TableColumnConfig<DossierWrapper>[] = [
|
||||
{
|
||||
label: _('dossier-listing.table-col-names.name'),
|
||||
sortByKey: 'dossierName'
|
||||
@ -81,7 +74,7 @@ export class DossierListingScreenComponent
|
||||
private _lastScrolledIndex: number;
|
||||
|
||||
@ViewChild('needsWorkTemplate', { read: TemplateRef, static: true })
|
||||
private readonly _needsWorkTemplate: TemplateRef<any>;
|
||||
private readonly _needsWorkTemplate: TemplateRef<unknown>;
|
||||
@ViewChild(CdkVirtualScrollViewport)
|
||||
private readonly _scrollViewport: CdkVirtualScrollViewport;
|
||||
|
||||
@ -102,7 +95,7 @@ export class DossierListingScreenComponent
|
||||
}
|
||||
|
||||
private get _activeDossiersCount(): number {
|
||||
return this.entitiesService.all.filter(p => p.dossier.status === Dossier.StatusEnum.ACTIVE).length;
|
||||
return this.entitiesService.all.filter(p => p.status === Dossier.StatusEnum.ACTIVE).length;
|
||||
}
|
||||
|
||||
private get _inactiveDossiersCount(): number {
|
||||
@ -123,11 +116,11 @@ export class DossierListingScreenComponent
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
ngAfterViewInit(): void {
|
||||
this.addSubscription = this._scrollViewport.scrolledIndexChange.pipe(tap(index => (this._lastScrolledIndex = index))).subscribe();
|
||||
}
|
||||
|
||||
ngOnAttach() {
|
||||
ngOnAttach(): void {
|
||||
this._appStateService.reset();
|
||||
this._loadEntitiesFromState();
|
||||
this.ngOnInit();
|
||||
@ -139,10 +132,6 @@ export class DossierListingScreenComponent
|
||||
this.ngOnDestroy();
|
||||
}
|
||||
|
||||
private _getDossierTemplate(dossierTemplateId: string): DossierTemplateModel {
|
||||
return this._appStateService.getDossierTemplateById(dossierTemplateId);
|
||||
}
|
||||
|
||||
openAddDossierDialog(): void {
|
||||
this._dialogService.openDialog('addDossier', null, null, async addResponse => {
|
||||
await this._router.navigate([`/main/dossiers/${addResponse.dossier.dossierId}`]);
|
||||
@ -155,7 +144,7 @@ export class DossierListingScreenComponent
|
||||
});
|
||||
}
|
||||
|
||||
calculateData() {
|
||||
calculateData(): void {
|
||||
this._computeAllFilters();
|
||||
|
||||
this.dossiersChartData = [
|
||||
@ -178,17 +167,7 @@ export class DossierListingScreenComponent
|
||||
}
|
||||
|
||||
private _loadEntitiesFromState() {
|
||||
const entities = this._appStateService.allDossiers.map(dossier => this._toListItem(dossier));
|
||||
this.entitiesService.setEntities(entities);
|
||||
}
|
||||
|
||||
private _toListItem(dossier: DossierWrapper): ListItem {
|
||||
return {
|
||||
dossierName: dossier.dossierName,
|
||||
routerLink: '/main/dossiers/' + dossier.dossierId.toString(),
|
||||
dossierTemplateName: this._getDossierTemplate(dossier.dossierTemplateId).name,
|
||||
dossier
|
||||
};
|
||||
this.entitiesService.setEntities(this._appStateService.allDossiers);
|
||||
}
|
||||
|
||||
private _computeAllFilters() {
|
||||
@ -199,18 +178,18 @@ export class DossierListingScreenComponent
|
||||
|
||||
this.entitiesService.all?.forEach(entry => {
|
||||
// all people
|
||||
entry.dossier.memberIds.forEach(f => allDistinctPeople.add(f));
|
||||
entry.memberIds.forEach(f => allDistinctPeople.add(f));
|
||||
// Needs work
|
||||
entry.dossier.files.forEach(file => {
|
||||
entry.files.forEach(file => {
|
||||
allDistinctFileStatus.add(file.status);
|
||||
if (file.analysisRequired) allDistinctNeedsWork.add('analysis');
|
||||
if (entry.dossier.hintsOnly) allDistinctNeedsWork.add('hint');
|
||||
if (entry.dossier.hasRedactions) allDistinctNeedsWork.add('redaction');
|
||||
if (entry.dossier.hasSuggestions) allDistinctNeedsWork.add('suggestion');
|
||||
if (entry.dossier.hasNone) allDistinctNeedsWork.add('none');
|
||||
if (entry.hintsOnly) allDistinctNeedsWork.add('hint');
|
||||
if (entry.hasRedactions) allDistinctNeedsWork.add('redaction');
|
||||
if (entry.hasSuggestions) allDistinctNeedsWork.add('suggestion');
|
||||
if (entry.hasNone) allDistinctNeedsWork.add('none');
|
||||
});
|
||||
|
||||
allDistinctDossierTemplates.add(entry.dossier.dossierTemplateId);
|
||||
allDistinctDossierTemplates.add(entry.dossierTemplateId);
|
||||
});
|
||||
|
||||
const statusFilters = [...allDistinctFileStatus].map<NestedFilter>(status => ({
|
||||
|
||||
@ -177,8 +177,6 @@
|
||||
[excludePages]="excludePages"
|
||||
[fileData]="fileData"
|
||||
[hideSkipped]="hideSkipped"
|
||||
[primaryFilters]="primaryFilters"
|
||||
[secondaryFilters]="secondaryFilters"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[viewer]="activeViewer"
|
||||
></redaction-file-workload>
|
||||
@ -194,3 +192,11 @@
|
||||
[viewer]="activeViewer"
|
||||
></redaction-annotation-actions>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #annotationFilterTemplate let-filter="filter">
|
||||
<redaction-type-filter *ngIf="filter.topLevelFilter" [filter]="filter"></redaction-type-filter>
|
||||
<ng-container *ngIf="!filter.topLevelFilter">
|
||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.key"></redaction-dictionary-annotation-icon>
|
||||
{{ filter.key | humanize: false }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { PdfViewerComponent } from '../../components/pdf-viewer/pdf-viewer.component';
|
||||
import { AutoUnsubscribe, CircleButtonTypes, Debounce, NestedFilter, processFilters } from '@iqser/common-ui';
|
||||
import { AutoUnsubscribe, CircleButtonTypes, Debounce, FilterService, NestedFilter, processFilters } from '@iqser/common-ui';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
@ -41,7 +41,8 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
|
||||
|
||||
@Component({
|
||||
templateUrl: './file-preview-screen.component.html',
|
||||
styleUrls: ['./file-preview-screen.component.scss']
|
||||
styleUrls: ['./file-preview-screen.component.scss'],
|
||||
providers: [FilterService]
|
||||
})
|
||||
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
@ -55,8 +56,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
fileData: FileDataModel;
|
||||
annotationData: AnnotationData;
|
||||
selectedAnnotations: AnnotationWrapper[];
|
||||
primaryFilters: NestedFilter[];
|
||||
secondaryFilters: NestedFilter[];
|
||||
canPerformAnnotationActions: boolean;
|
||||
hideSkipped = false;
|
||||
displayPDFViewer = false;
|
||||
@ -67,6 +66,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private _lastPage: string;
|
||||
private _reloadFileOnReanalysis = false;
|
||||
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
|
||||
@ViewChild('annotationFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
static: true
|
||||
})
|
||||
private readonly _filterTemplate: TemplateRef<NestedFilter>;
|
||||
|
||||
constructor(
|
||||
readonly appStateService: AppStateService,
|
||||
@ -87,6 +91,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _fileManagementControllerService: FileManagementControllerService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _filterService: FilterService,
|
||||
private readonly _translateService: TranslateService
|
||||
) {
|
||||
super();
|
||||
@ -269,11 +274,16 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this.userPreferenceService.areDevFeaturesEnabled
|
||||
);
|
||||
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
|
||||
this.primaryFilters = processFilters(this.primaryFilters, annotationFilters);
|
||||
this.secondaryFilters = processFilters(this.secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters);
|
||||
this._workloadComponent?.filtersChanged({
|
||||
primary: this.primaryFilters,
|
||||
secondary: this.secondaryFilters
|
||||
const primaryFilters = this._filterService.getGroup('primaryFilters')?.filters;
|
||||
this._filterService.addFilterGroup({
|
||||
slug: 'primaryFilters',
|
||||
filterTemplate: this._filterTemplate,
|
||||
filters: processFilters(primaryFilters, annotationFilters)
|
||||
});
|
||||
const secondaryFilters = this._filterService.getGroup('secondaryFilters')?.filters;
|
||||
this._filterService.addFilterGroup({
|
||||
slug: 'secondaryFilters',
|
||||
filters: processFilters(secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters)
|
||||
});
|
||||
console.log('[REDACTION] Process time: ' + (new Date().getTime() - processStartTime) + 'ms');
|
||||
console.log(
|
||||
@ -625,8 +635,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private _handleDeltaAnnotationFilters(currentPageAnnotations: AnnotationWrapper[], newPageAnnotations: AnnotationWrapper[]) {
|
||||
const hasAnyFilterSet =
|
||||
this.primaryFilters.find(f => f.checked || f.indeterminate) || this.secondaryFilters.find(f => f.checked || f.indeterminate);
|
||||
const primaryFilterGroup = this._filterService.getGroup('primaryFilters');
|
||||
const primaryFilters = primaryFilterGroup.filters;
|
||||
const secondaryFilters = this._filterService.getGroup('secondaryFilters').filters;
|
||||
const hasAnyFilterSet = [...primaryFilters, ...secondaryFilters].find(f => f.checked || f.indeterminate);
|
||||
|
||||
if (!hasAnyFilterSet) {
|
||||
return;
|
||||
@ -634,10 +646,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
const oldPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(currentPageAnnotations);
|
||||
const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(newPageAnnotations);
|
||||
handleFilterDelta(oldPageSpecificFilters, newPageSpecificFilters, this.primaryFilters);
|
||||
this._workloadComponent.filtersChanged({
|
||||
primary: this.primaryFilters,
|
||||
secondary: this.secondaryFilters
|
||||
handleFilterDelta(oldPageSpecificFilters, newPageSpecificFilters, primaryFilters);
|
||||
this._filterService.addFilterGroup({
|
||||
...primaryFilterGroup,
|
||||
filters: primaryFilters
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,101 +1,109 @@
|
||||
<iqser-icon-button
|
||||
*ngIf="icon"
|
||||
[icon]="icon"
|
||||
[label]="filterLabel || ('filter-menu.label' | translate)"
|
||||
[matMenuTriggerFor]="filterMenu"
|
||||
[showDot]="hasActiveFilters"
|
||||
></iqser-icon-button>
|
||||
<ng-container *ngIf="primaryFilterGroup$ | async as primaryGroup">
|
||||
<iqser-icon-button
|
||||
*ngIf="primaryGroup.icon"
|
||||
[icon]="primaryGroup.icon"
|
||||
[label]="primaryGroup.label || ('filter-menu.label' | translate)"
|
||||
[matMenuTriggerFor]="filterMenu"
|
||||
[showDot]="hasActiveFilters$ | async"
|
||||
[attr.aria-expanded]="expanded$ | async"
|
||||
></iqser-icon-button>
|
||||
|
||||
<iqser-chevron-button
|
||||
*ngIf="!icon"
|
||||
[label]="filterLabel || ('filter-menu.label' | translate)"
|
||||
[matMenuTriggerFor]="filterMenu"
|
||||
[showDot]="hasActiveFilters"
|
||||
></iqser-chevron-button>
|
||||
<iqser-chevron-button
|
||||
*ngIf="!primaryGroup.icon"
|
||||
[label]="primaryGroup.label || ('filter-menu.label' | translate)"
|
||||
[matMenuTriggerFor]="filterMenu"
|
||||
[showDot]="hasActiveFilters$ | async"
|
||||
[attr.aria-expanded]="expanded$ | async"
|
||||
></iqser-chevron-button>
|
||||
|
||||
<mat-menu #filterMenu="matMenu" (closed)="applyFilters()" [class.padding-bottom-0]="secondaryFilters?.length > 0" xPosition="before">
|
||||
<ng-template matMenuContent>
|
||||
<div class="filter-menu-header">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-types"></div>
|
||||
<div class="actions">
|
||||
<div
|
||||
(click)="activatePrimaryFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.all"
|
||||
></div>
|
||||
<div
|
||||
(click)="deactivateFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.none"
|
||||
></div>
|
||||
<mat-menu
|
||||
#filterMenu="matMenu"
|
||||
(close)="expanded.next(false)"
|
||||
[class]="(secondaryFilterGroup$ | async)?.filters.length > 0 ? 'padding-bottom-0' : ''"
|
||||
xPosition="before"
|
||||
>
|
||||
<ng-template matMenuContent>
|
||||
<div class="filter-menu-header">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-types"></div>
|
||||
<div class="actions">
|
||||
<div
|
||||
(click)="activatePrimaryFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.all"
|
||||
></div>
|
||||
<div
|
||||
(click)="deactivateFilters(); $event.stopPropagation()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="actions.none"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-content">
|
||||
<div *ngFor="let filter of primaryFilters">
|
||||
<div class="filter-content">
|
||||
<ng-container
|
||||
*ngFor="let filter of primaryGroup.filters"
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
atLeastOneIsExpandable: atLeastOneFilterIsExpandable
|
||||
atLeastOneIsExpandable: atLeastOneFilterIsExpandable$ | async
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="secondaryFilterGroup$ | async as secondaryGroup" class="filter-options">
|
||||
<div class="filter-menu-options">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-options"></div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let filter of secondaryGroup.filters">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
atLeastOneIsExpandable: atLeastOneSecondaryFilterIsExpandable$ | async
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
||||
<ng-template #defaultFilterLabelTemplate let-filter="filter">
|
||||
{{ filter?.label }}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #defaultFilterTemplate let-atLeastOneIsExpandable="atLeastOneIsExpandable" let-filter="filter">
|
||||
<div (click)="toggleFilterExpanded($event, filter)" class="mat-menu-item flex">
|
||||
<div *ngIf="filter.children?.length > 0" class="arrow-wrapper">
|
||||
<mat-icon *ngIf="filter.expanded" color="accent" svgIcon="iqser:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="!filter.expanded" color="accent" svgIcon="red:arrow-right"></mat-icon>
|
||||
</div>
|
||||
<div *ngIf="atLeastOneIsExpandable && filter.children?.length === 0" class="arrow-wrapper spacer"> </div>
|
||||
<mat-checkbox
|
||||
(click)="filterCheckboxClicked($event, filter)"
|
||||
[checked]="filter.checked"
|
||||
[indeterminate]="filter.indeterminate"
|
||||
class="filter-menu-checkbox"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: filter }"
|
||||
[ngTemplateOutlet]="primaryGroup.filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: filter }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="secondaryFilters?.length > 0" class="filter-options">
|
||||
<div class="filter-menu-options">
|
||||
<div class="all-caps-label" translate="filter-menu.filter-options"></div>
|
||||
</div>
|
||||
<div *ngIf="filter.children?.length && filter.expanded">
|
||||
<div (click)="$event.stopPropagation()" *ngFor="let child of filter.children" class="padding-left mat-menu-item">
|
||||
<mat-checkbox (click)="filterCheckboxClicked($event, child, filter)" [checked]="child.checked">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: child }"
|
||||
[ngTemplateOutlet]="primaryGroup.filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
|
||||
<div *ngFor="let filter of secondaryFilters">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{
|
||||
filter: filter,
|
||||
atLeastOneIsExpandable: atLeastOneSecondaryFilterIsExpandable
|
||||
}"
|
||||
[ngTemplateOutlet]="defaultFilterTemplate"
|
||||
></ng-container>
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: child }" [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
|
||||
<ng-template #defaultFilterLabelTemplate let-filter="filter">
|
||||
{{ _(filter)?.label }}
|
||||
</ng-template>
|
||||
|
||||
<ng-template #defaultFilterTemplate let-atLeastOneIsExpandable="atLeastOneIsExpandable" let-filter="filter">
|
||||
<div (click)="toggleFilterExpanded($event, filter)" class="mat-menu-item flex">
|
||||
<div *ngIf="isExpandable(filter)" class="arrow-wrapper">
|
||||
<mat-icon *ngIf="_(filter).expanded" color="accent" svgIcon="iqser:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="!_(filter).expanded" color="accent" svgIcon="red:arrow-right"></mat-icon>
|
||||
</div>
|
||||
<div *ngIf="atLeastOneIsExpandable && !isExpandable(filter)" class="arrow-wrapper spacer"> </div>
|
||||
<mat-checkbox
|
||||
(click)="filterCheckboxClicked($event, filter)"
|
||||
[checked]="_(filter).checked"
|
||||
[indeterminate]="_(filter).indeterminate"
|
||||
class="filter-menu-checkbox"
|
||||
>
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: filter }"
|
||||
[ngTemplateOutlet]="filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: filter }" [ngTemplateOutlet]="actionsTemplate ?? null"></ng-container>
|
||||
</div>
|
||||
|
||||
<div *ngIf="_(filter).children?.length && _(filter).expanded">
|
||||
<div (click)="$event.stopPropagation()" *ngFor="let subFilter of _(filter).children" class="padding-left mat-menu-item">
|
||||
<mat-checkbox (click)="filterCheckboxClicked($event, subFilter, filter)" [checked]="subFilter.checked">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ filter: subFilter }"
|
||||
[ngTemplateOutlet]="filterTemplate ?? defaultFilterLabelTemplate"
|
||||
></ng-container>
|
||||
</mat-checkbox>
|
||||
|
||||
<ng-container [ngTemplateOutletContext]="{ filter: subFilter }" [ngTemplateOutlet]="actionsTemplate ?? null"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, TemplateRef } from '@angular/core';
|
||||
import { NestedFilter } from '@iqser/common-ui';
|
||||
import { handleCheckedValue } from '@iqser/common-ui';
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit, TemplateRef } from '@angular/core';
|
||||
import { FilterGroup, FilterService, handleCheckedValue, NestedFilter } from '@iqser/common-ui';
|
||||
import { MAT_CHECKBOX_DEFAULT_OPTIONS } from '@angular/material/checkbox';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
import { delay, distinctUntilChanged, map, shareReplay } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-popup-filter',
|
||||
templateUrl: './popup-filter.component.html',
|
||||
styleUrls: ['./popup-filter.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
providers: [
|
||||
{
|
||||
provide: MAT_CHECKBOX_DEFAULT_OPTIONS,
|
||||
@ -17,88 +19,84 @@ import { MAT_CHECKBOX_DEFAULT_OPTIONS } from '@angular/material/checkbox';
|
||||
}
|
||||
]
|
||||
})
|
||||
export class PopupFilterComponent implements OnChanges {
|
||||
@Output() filtersChanged = new EventEmitter<{
|
||||
primary: NestedFilter[];
|
||||
secondary?: NestedFilter[];
|
||||
}>();
|
||||
@Input() filterTemplate: TemplateRef<any>;
|
||||
@Input() actionsTemplate: TemplateRef<any>;
|
||||
@Input() primaryFilters: NestedFilter[] = [];
|
||||
@Input() secondaryFilters: NestedFilter[] = [];
|
||||
@Input() filterLabel;
|
||||
@Input() icon: string;
|
||||
export class PopupFilterComponent implements OnInit {
|
||||
@Input() actionsTemplate: TemplateRef<unknown>;
|
||||
@Input() primaryFiltersSlug: string;
|
||||
@Input() secondaryFiltersSlug: string;
|
||||
|
||||
atLeastOneFilterIsExpandable = false;
|
||||
atLeastOneSecondaryFilterIsExpandable = false;
|
||||
atLeastOneFilterIsExpandable$?: Observable<boolean>;
|
||||
atLeastOneSecondaryFilterIsExpandable$?: Observable<boolean>;
|
||||
hasActiveFilters$?: Observable<boolean>;
|
||||
readonly expanded = new BehaviorSubject<boolean>(null);
|
||||
readonly expanded$ = this.expanded.asObservable().pipe(delay(200));
|
||||
|
||||
constructor(private readonly _changeDetectorRef: ChangeDetectorRef) {}
|
||||
primaryFilterGroup$?: Observable<FilterGroup>;
|
||||
secondaryFilterGroup$?: Observable<FilterGroup>;
|
||||
|
||||
get hasActiveFilters(): boolean {
|
||||
return !!this._allFilters.find(f => f.checked || f.indeterminate);
|
||||
constructor(readonly filterService: FilterService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.primaryFilterGroup$ = this.filterService.getGroup$(this.primaryFiltersSlug);
|
||||
this.secondaryFilterGroup$ = this.filterService.getGroup$(this.secondaryFiltersSlug);
|
||||
this.hasActiveFilters$ = combineLatest([this.primaryFilterGroup$, this.secondaryFilterGroup$]).pipe(
|
||||
map(([primary, secondary]) => [...primary.filters, ...(secondary?.filters || [])]),
|
||||
map(filters => filters.some(f => f.checked || f.indeterminate)),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
this.atLeastOneFilterIsExpandable$ = this.primaryFilterGroup$.pipe(
|
||||
map(group => group.filters.some(f => this.isExpandable(f))),
|
||||
distinctUntilChanged(),
|
||||
shareReplay()
|
||||
);
|
||||
this.atLeastOneSecondaryFilterIsExpandable$ = this.secondaryFilterGroup$.pipe(
|
||||
map(group => group?.filters.some(f => this.isExpandable(f))),
|
||||
distinctUntilChanged(),
|
||||
shareReplay()
|
||||
);
|
||||
}
|
||||
|
||||
private get _allFilters(): NestedFilter[] {
|
||||
return [...(this.primaryFilters ?? []), ...(this.secondaryFilters ?? [])];
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.atLeastOneFilterIsExpandable = !!this.primaryFilters?.find(f => this.isExpandable(f));
|
||||
this.atLeastOneSecondaryFilterIsExpandable = !!this.secondaryFilters?.find(f => this.isExpandable(f));
|
||||
}
|
||||
|
||||
filterCheckboxClicked($event: any, filter: NestedFilter, parent?: NestedFilter) {
|
||||
filterCheckboxClicked($event: MouseEvent, nestedFilter: NestedFilter, parent?: NestedFilter): void {
|
||||
$event.stopPropagation();
|
||||
|
||||
filter.checked = !filter.checked;
|
||||
nestedFilter.checked = !nestedFilter.checked;
|
||||
|
||||
if (parent) {
|
||||
handleCheckedValue(parent);
|
||||
} else {
|
||||
if (filter.indeterminate) filter.checked = false;
|
||||
filter.indeterminate = false;
|
||||
filter.children?.forEach(f => (f.checked = filter.checked));
|
||||
if (nestedFilter.indeterminate) nestedFilter.checked = false;
|
||||
nestedFilter.indeterminate = false;
|
||||
nestedFilter.children?.forEach(f => (f.checked = nestedFilter.checked));
|
||||
}
|
||||
|
||||
this.applyFilters();
|
||||
this.filterService.refresh();
|
||||
}
|
||||
|
||||
activatePrimaryFilters() {
|
||||
this._setFilters(true);
|
||||
activatePrimaryFilters(): void {
|
||||
this._setFilters(this.primaryFiltersSlug, true);
|
||||
}
|
||||
|
||||
deactivateFilters() {
|
||||
this._setFilters();
|
||||
deactivateFilters(): void {
|
||||
this._setFilters(this.primaryFiltersSlug);
|
||||
if (this.secondaryFiltersSlug) this._setFilters(this.secondaryFiltersSlug);
|
||||
}
|
||||
|
||||
applyFilters() {
|
||||
this.filtersChanged.emit({
|
||||
primary: this.primaryFilters,
|
||||
secondary: this.secondaryFilters
|
||||
});
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
toggleFilterExpanded($event: MouseEvent, filter: NestedFilter) {
|
||||
toggleFilterExpanded($event: MouseEvent, nestedFilter: NestedFilter): void {
|
||||
$event.stopPropagation();
|
||||
filter.expanded = !filter.expanded;
|
||||
nestedFilter.expanded = !nestedFilter.expanded;
|
||||
this.filterService.refresh();
|
||||
}
|
||||
|
||||
isExpandable(filter: NestedFilter) {
|
||||
return filter?.children?.length > 0;
|
||||
isExpandable(nestedFilter: NestedFilter): boolean {
|
||||
return nestedFilter?.children?.length > 0;
|
||||
}
|
||||
|
||||
_(obj): NestedFilter {
|
||||
return obj as NestedFilter;
|
||||
}
|
||||
|
||||
private _setFilters(onlyPrimaryFilters = false) {
|
||||
const filters = onlyPrimaryFilters ? this.primaryFilters : this._allFilters;
|
||||
private _setFilters(filterGroup: string, checked = false) {
|
||||
const filters = this.filterService.getGroup(filterGroup).filters;
|
||||
filters.forEach(f => {
|
||||
f.checked = onlyPrimaryFilters;
|
||||
f.checked = checked;
|
||||
f.indeterminate = false;
|
||||
f.children?.forEach(ff => (ff.checked = onlyPrimaryFilters));
|
||||
f.children?.forEach(ff => (ff.checked = checked));
|
||||
});
|
||||
this.applyFilters();
|
||||
this.filterService.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ export class InitialsAvatarComponent extends AutoUnsubscribe implements OnChange
|
||||
}
|
||||
|
||||
get disabled(): boolean {
|
||||
return this.user && !this.user?.isActive;
|
||||
return this.user && !this.user.isActive;
|
||||
}
|
||||
|
||||
private get _colorClass() {
|
||||
|
||||
@ -7,14 +7,7 @@
|
||||
<ng-container *ngIf="searchPosition === searchPositions.beforeFilters" [ngTemplateOutlet]="searchBar"></ng-container>
|
||||
|
||||
<ng-container *ngFor="let config of filters; trackBy: trackByLabel">
|
||||
<redaction-popup-filter
|
||||
(filtersChanged)="filterService.refresh()"
|
||||
*ngIf="!config.hide"
|
||||
[filterLabel]="config.label"
|
||||
[filterTemplate]="config.filterTemplate"
|
||||
[icon]="config.icon"
|
||||
[primaryFilters]="config.filters"
|
||||
></redaction-popup-filter>
|
||||
<redaction-popup-filter *ngIf="!config.hide" [primaryFiltersSlug]="config.slug"></redaction-popup-filter>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="searchPosition === searchPositions.afterFilters" [ngTemplateOutlet]="searchBar"></ng-container>
|
||||
|
||||
@ -11,7 +11,7 @@ import { SearchPosition, SearchPositions } from '@shared/components/page-header/
|
||||
templateUrl: './page-header.component.html',
|
||||
styleUrls: ['./page-header.component.scss']
|
||||
})
|
||||
export class PageHeaderComponent<T extends object> {
|
||||
export class PageHeaderComponent<T> {
|
||||
readonly searchPositions = SearchPositions;
|
||||
|
||||
@Input() pageLabel: string;
|
||||
|
||||
@ -222,7 +222,9 @@ export class AppStateService {
|
||||
return;
|
||||
}
|
||||
|
||||
const mappedDossiers = dossiers.map(p => new DossierWrapper(p, this._getExistingFiles(p.dossierId)));
|
||||
const mappedDossiers = dossiers.map(
|
||||
p => new DossierWrapper(p, this.getDossierTemplateById(p.dossierTemplateId).name, this._getExistingFiles(p.dossierId))
|
||||
);
|
||||
const fileData = await this._statusControllerService.getFileStatusForDossiers(mappedDossiers.map(p => p.dossierId)).toPromise();
|
||||
|
||||
for (const dossierId of Object.keys(fileData)) {
|
||||
@ -348,9 +350,13 @@ export class AppStateService {
|
||||
let foundDossier = this.allDossiers.find(p => p.dossierId === updatedDossier.dossierId);
|
||||
if (foundDossier) {
|
||||
this._appState.dossiers.splice(this._appState.dossiers.indexOf(foundDossier), 1);
|
||||
foundDossier = new DossierWrapper(updatedDossier, foundDossier.files);
|
||||
foundDossier = new DossierWrapper(
|
||||
updatedDossier,
|
||||
this.getDossierTemplateById(updatedDossier.dossierTemplateId).name,
|
||||
foundDossier.files
|
||||
);
|
||||
} else {
|
||||
foundDossier = new DossierWrapper(updatedDossier, []);
|
||||
foundDossier = new DossierWrapper(updatedDossier, this.getDossierTemplateById(updatedDossier.dossierTemplateId).name, []);
|
||||
}
|
||||
|
||||
this._appState.dossiers.push(foundDossier);
|
||||
|
||||
@ -38,7 +38,7 @@ export class DossierWrapper implements Dossier {
|
||||
allFilesApproved?: boolean;
|
||||
type?: Dictionary;
|
||||
|
||||
constructor(private readonly _dossier: Dossier, private _files: FileStatusWrapper[] = []) {
|
||||
constructor(private readonly _dossier: Dossier, readonly dossierTemplateName, private _files: FileStatusWrapper[] = []) {
|
||||
this._recomputeFileStatus();
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ export class DossierWrapper implements Dossier {
|
||||
return !!this._files.find(f => f.status === status);
|
||||
}
|
||||
|
||||
hasMember(key: string) {
|
||||
return this._dossier.memberIds.indexOf(key) >= 0;
|
||||
hasMember(memberId: string) {
|
||||
return this._dossier.memberIds.indexOf(memberId) >= 0;
|
||||
}
|
||||
|
||||
addedDateMatches(key: string) {
|
||||
|
||||
@ -103,7 +103,9 @@
|
||||
"cypress-localstorage-commands": "^1.4.5",
|
||||
"dotenv": "10.0.0",
|
||||
"eslint": "7.28.0",
|
||||
"eslint-config-airbnb-typescript": "^12.3.1",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-import": "2.23.4",
|
||||
"google-translate-api-browser": "^1.1.71",
|
||||
"husky": "4.3.8",
|
||||
|
||||
101
yarn.lock
101
yarn.lock
@ -3333,6 +3333,16 @@
|
||||
"@typescript-eslint/typescript-estree" "4.26.1"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@typescript-eslint/parser@^4.4.1":
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.29.1.tgz#17dfbb45c9032ffa0fe15881d20fbc2a4bdeb02d"
|
||||
integrity sha512-3fL5iN20hzX3Q4OkG7QEPFjZV2qsVGiDhEwwh+EkmE/w7oteiOvUNzmpu5eSwGJX/anCryONltJ3WDmAzAoCMg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "4.29.1"
|
||||
"@typescript-eslint/types" "4.29.1"
|
||||
"@typescript-eslint/typescript-estree" "4.29.1"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.22.1":
|
||||
version "4.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz#5bb357f94f9cd8b94e6be43dd637eb73b8f355b4"
|
||||
@ -3357,6 +3367,14 @@
|
||||
"@typescript-eslint/types" "4.26.1"
|
||||
"@typescript-eslint/visitor-keys" "4.26.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.29.1":
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.1.tgz#f25da25bc6512812efa2ce5ebd36619d68e61358"
|
||||
integrity sha512-Hzv/uZOa9zrD/W5mftZa54Jd5Fed3tL6b4HeaOpwVSabJK8CJ+2MkDasnX/XK4rqP5ZTWngK1ZDeCi6EnxPQ7A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.29.1"
|
||||
"@typescript-eslint/visitor-keys" "4.29.1"
|
||||
|
||||
"@typescript-eslint/types@4.22.1":
|
||||
version "4.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a"
|
||||
@ -3372,6 +3390,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.26.1.tgz#9e7c523f73c34b04a765e4167ca5650436ef1d38"
|
||||
integrity sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==
|
||||
|
||||
"@typescript-eslint/types@4.29.1":
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.1.tgz#94cce6cf7cc83451df03339cda99d326be2feaf5"
|
||||
integrity sha512-Jj2yu78IRfw4nlaLtKjVaGaxh/6FhofmQ/j8v3NXmAiKafbIqtAPnKYrf0sbGjKdj0hS316J8WhnGnErbJ4RCA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.22.1":
|
||||
version "4.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9"
|
||||
@ -3411,6 +3434,19 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.29.1":
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.1.tgz#7b32a25ff8e51f2671ccc6b26cdbee3b1e6c5e7f"
|
||||
integrity sha512-lIkkrR9E4lwZkzPiRDNq0xdC3f2iVCUjw/7WPJ4S2Sl6C3nRWkeE1YXCQ0+KsiaQRbpY16jNaokdWnm9aUIsfw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.29.1"
|
||||
"@typescript-eslint/visitor-keys" "4.29.1"
|
||||
debug "^4.3.1"
|
||||
globby "^11.0.3"
|
||||
is-glob "^4.0.1"
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.22.1":
|
||||
version "4.22.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6"
|
||||
@ -3435,6 +3471,14 @@
|
||||
"@typescript-eslint/types" "4.26.1"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.29.1":
|
||||
version "4.29.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.1.tgz#0615be8b55721f5e854f3ee99f1a714f2d093e5d"
|
||||
integrity sha512-zLqtjMoXvgdZY/PG6gqA73V8BjqPs4af1v2kiiETBObp+uC6gRYnJLmJHxC0QyUrrHDLJPIWNYxoBV3wbcRlag==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.29.1"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@webassemblyjs/ast@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
|
||||
@ -5234,7 +5278,7 @@ concat-stream@^1.5.0, concat-stream@^1.6.2:
|
||||
readable-stream "^2.2.2"
|
||||
typedarray "^0.0.6"
|
||||
|
||||
confusing-browser-globals@^1.0.9:
|
||||
confusing-browser-globals@^1.0.10, confusing-browser-globals@^1.0.9:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59"
|
||||
integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA==
|
||||
@ -6572,6 +6616,33 @@ escodegen@^2.0.0:
|
||||
optionalDependencies:
|
||||
source-map "~0.6.1"
|
||||
|
||||
eslint-config-airbnb-base@^14.2.0, eslint-config-airbnb-base@^14.2.1:
|
||||
version "14.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e"
|
||||
integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==
|
||||
dependencies:
|
||||
confusing-browser-globals "^1.0.10"
|
||||
object.assign "^4.1.2"
|
||||
object.entries "^1.1.2"
|
||||
|
||||
eslint-config-airbnb-typescript@^12.3.1:
|
||||
version "12.3.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-12.3.1.tgz#83ab40d76402c208eb08516260d1d6fac8f8acbc"
|
||||
integrity sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==
|
||||
dependencies:
|
||||
"@typescript-eslint/parser" "^4.4.1"
|
||||
eslint-config-airbnb "^18.2.0"
|
||||
eslint-config-airbnb-base "^14.2.0"
|
||||
|
||||
eslint-config-airbnb@^18.2.0:
|
||||
version "18.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz#b7fe2b42f9f8173e825b73c8014b592e449c98d9"
|
||||
integrity sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==
|
||||
dependencies:
|
||||
eslint-config-airbnb-base "^14.2.1"
|
||||
object.assign "^4.1.2"
|
||||
object.entries "^1.1.2"
|
||||
|
||||
eslint-config-prettier@8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a"
|
||||
@ -6614,6 +6685,13 @@ eslint-plugin-import@2.23.4:
|
||||
resolve "^1.20.0"
|
||||
tsconfig-paths "^3.9.0"
|
||||
|
||||
eslint-plugin-prettier@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7"
|
||||
integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
eslint-scope@5.1.1, eslint-scope@^5.0.0, eslint-scope@^5.1.0, eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
@ -6991,6 +7069,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-diff@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||
|
||||
fast-glob@^3.1.1:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
|
||||
@ -10536,6 +10619,15 @@ object.assign@^4.1.2:
|
||||
has-symbols "^1.0.1"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
object.entries@^1.1.2:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd"
|
||||
integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.18.2"
|
||||
|
||||
object.pick@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
|
||||
@ -11734,6 +11826,13 @@ prelude-ls@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
|
||||
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
|
||||
|
||||
prettier-linter-helpers@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user