Refactored file actions & event emitters
This commit is contained in:
parent
85d7c59c3a
commit
dd319dfee9
@ -1,11 +1,11 @@
|
||||
<div *ngIf="!excludePages" class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div *ngIf="!showExcludedPages" class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<div
|
||||
(click)="multiSelectActive = true"
|
||||
*ngIf="!multiSelectActive && !isReadOnly"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="file-preview.tabs.annotations.select"
|
||||
iqserHelpMode="bulk-select-annotations"
|
||||
translate="file-preview.tabs.annotations.select"
|
||||
></div>
|
||||
<iqser-popup-filter
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
@ -16,10 +16,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="excludePages" class="right-title heading" translate="file-preview.tabs.exclude-pages.label">
|
||||
<div *ngIf="showExcludedPages" class="right-title heading" translate="file-preview.tabs.exclude-pages.label">
|
||||
<div>
|
||||
<iqser-circle-button
|
||||
(action)="actionPerformed.emit('view-exclude-pages')"
|
||||
(action)="toggleViewExcludedPages.emit()"
|
||||
[tooltip]="'file-preview.tabs.exclude-pages.close' | translate"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="before"
|
||||
@ -111,11 +111,11 @@
|
||||
</div>
|
||||
|
||||
<div style="overflow: hidden; width: 100%">
|
||||
<ng-container *ngIf="!excludePages">
|
||||
<ng-container *ngIf="!showExcludedPages">
|
||||
<div [attr.anotation-page-header]="activeViewerPage" [class.padding-left-0]="currentPageIsExcluded" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="flex-align-items-center">
|
||||
<iqser-circle-button
|
||||
(action)="viewExcludePages()"
|
||||
(action)="toggleViewExcludedPages.emit()"
|
||||
*ngIf="currentPageIsExcluded"
|
||||
[tooltip]="'file-preview.excluded-from-redaction' | translate | capitalize"
|
||||
icon="red:exclude-pages"
|
||||
@ -161,7 +161,8 @@
|
||||
>
|
||||
<ng-container *ngIf="currentPageIsExcluded">
|
||||
{{ 'file-preview.tabs.annotations.page-is' | translate }}
|
||||
<a (click)="viewExcludePages()" class="with-underline" translate="file-preview.excluded-from-redaction"></a
|
||||
<a (click)="toggleViewExcludedPages.emit()" class="with-underline"
|
||||
translate="file-preview.excluded-from-redaction"></a
|
||||
>.
|
||||
</ng-container>
|
||||
</iqser-empty-state>
|
||||
@ -201,8 +202,8 @@
|
||||
</ng-container>
|
||||
|
||||
<redaction-page-exclusion
|
||||
(actionPerformed)="actionPerformed.emit($event)"
|
||||
*ngIf="excludePages"
|
||||
(excludePages)="excludePages.emit()"
|
||||
*ngIf="showExcludedPages"
|
||||
[file]="file"
|
||||
></redaction-page-exclusion>
|
||||
</div>
|
||||
|
||||
@ -43,7 +43,7 @@ export class FileWorkloadComponent {
|
||||
@Input() @Required() file!: File;
|
||||
@Input() @Required() dossier!: Dossier;
|
||||
@Input() hideSkipped: boolean;
|
||||
@Input() excludePages: boolean;
|
||||
@Input() showExcludedPages: boolean;
|
||||
@Input() annotationActionsTemplate: TemplateRef<unknown>;
|
||||
@Input() viewer: WebViewerInstance;
|
||||
@Output() readonly shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
|
||||
@ -54,10 +54,11 @@ export class FileWorkloadComponent {
|
||||
@Output() readonly selectPage = new EventEmitter<number>();
|
||||
@Output() readonly toggleSkipped = new EventEmitter<MouseEvent>();
|
||||
@Output() readonly annotationsChanged = new EventEmitter<AnnotationWrapper>();
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
@Output() readonly excludePages = new EventEmitter<void>();
|
||||
displayedPages: number[] = [];
|
||||
pagesPanelActive = true;
|
||||
readonly displayedAnnotations$: Observable<Map<number, AnnotationWrapper[]>>;
|
||||
@Output() @Required() readonly toggleViewExcludedPages = new EventEmitter<void>();
|
||||
private _annotations$ = new BehaviorSubject<AnnotationWrapper[]>([]);
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
@ -244,10 +245,6 @@ export class FileWorkloadComponent {
|
||||
this.selectPage.emit(this._nextPageWithAnnotations());
|
||||
}
|
||||
|
||||
viewExcludePages(): void {
|
||||
this.actionPerformed.emit('view-exclude-pages');
|
||||
}
|
||||
|
||||
private _filterAnnotations(
|
||||
annotations: AnnotationWrapper[],
|
||||
primary: INestedFilter[],
|
||||
|
||||
@ -12,7 +12,7 @@ import { ReanalysisService } from '@services/reanalysis.service';
|
||||
})
|
||||
export class PageExclusionComponent implements OnChanges {
|
||||
@Input() file: File;
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
@Output() readonly excludePages = new EventEmitter<void>();
|
||||
|
||||
excludedPagesRanges: IPageRange[] = [];
|
||||
@ViewChild(InputWithActionComponent) private readonly _inputComponent: InputWithActionComponent;
|
||||
@ -66,7 +66,7 @@ export class PageExclusionComponent implements OnChanges {
|
||||
)
|
||||
.toPromise();
|
||||
this._inputComponent.reset();
|
||||
this.actionPerformed.emit('exclude-pages');
|
||||
this.excludePages.emit();
|
||||
} catch (e) {
|
||||
this._toaster.error(_('file-preview.tabs.exclude-pages.error'));
|
||||
this._loadingService.stop();
|
||||
@ -85,6 +85,6 @@ export class PageExclusionComponent implements OnChanges {
|
||||
)
|
||||
.toPromise();
|
||||
this._inputComponent.reset();
|
||||
this.actionPerformed.emit('exclude-pages');
|
||||
this.excludePages.emit();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Toaster } from '@iqser/common-ui';
|
||||
import { LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -28,9 +27,9 @@ export class AssignReviewerApproverDialogComponent {
|
||||
readonly userService: UserService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _dialogRef: MatDialogRef<AssignReviewerApproverDialogComponent, boolean>,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: DialogData,
|
||||
@ -68,36 +67,6 @@ export class AssignReviewerApproverDialogComponent {
|
||||
return this.data.files.reduce((prev, file) => prev && this.permissionsService.canUnassignUser(file), true);
|
||||
}
|
||||
|
||||
isOwner(userId: string): boolean {
|
||||
return userId === this.selectedUser;
|
||||
}
|
||||
|
||||
async save() {
|
||||
try {
|
||||
if (this.data.mode === 'reviewer') {
|
||||
await this._filesService
|
||||
.setReviewerFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
this.dossier.id,
|
||||
this.selectedUser,
|
||||
)
|
||||
.toPromise();
|
||||
} else {
|
||||
await this._filesService
|
||||
.setUnderApprovalFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
this.dossier.id,
|
||||
this.selectedUser,
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
} catch (error) {
|
||||
this._toaster.error(_('error.http.generic'), { params: error });
|
||||
}
|
||||
|
||||
this._dialogRef.close(true);
|
||||
}
|
||||
|
||||
/** Initialize the form with:
|
||||
* the id of the current reviewer of the files list if there is only one reviewer for all of them;
|
||||
* or the id of the current user
|
||||
@ -119,6 +88,38 @@ export class AssignReviewerApproverDialogComponent {
|
||||
return user;
|
||||
}
|
||||
|
||||
isOwner(userId: string): boolean {
|
||||
return userId === this.selectedUser;
|
||||
}
|
||||
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
if (this.data.mode === 'reviewer') {
|
||||
await this._filesService
|
||||
.setReviewerFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
this.dossier.id,
|
||||
this.selectedUser,
|
||||
)
|
||||
.toPromise();
|
||||
} else {
|
||||
await this._filesService
|
||||
.setUnderApprovalFor(
|
||||
this.data.files.map(f => f.fileId),
|
||||
this.dossier.id,
|
||||
this.selectedUser,
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
} catch (error) {
|
||||
this._toaster.error(_('error.http.generic'), { params: error });
|
||||
}
|
||||
this._loadingService.stop();
|
||||
|
||||
this._dialogRef.close(true);
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
return this._formBuilder.group({
|
||||
// Allow a null reviewer if a previous reviewer exists (= it's not the first assignment) & current user is allowed to unassign
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="listingService.selectedLength$ | async" redactionLongPress>
|
||||
<ng-container (longPress)="forceReanalysisAction($event)" *ngIf="selectedFiles.length" redactionLongPress>
|
||||
<iqser-circle-button
|
||||
(action)="delete()"
|
||||
*ngIf="canDelete"
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { FileAssignService } from '../../../../shared/services/file-assign.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
||||
import { CircleButtonTypes, ConfirmationDialogInput, ListingService, LoadingService } from '@iqser/common-ui';
|
||||
import { CircleButtonTypes, ConfirmationDialogInput, LoadingService, Required } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
@ -22,14 +20,11 @@ import { FilesService } from '@services/entity-services/files.service';
|
||||
})
|
||||
export class DossierOverviewBulkActionsComponent {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
@Input() dossier: Dossier;
|
||||
@Output() readonly reload = new EventEmitter();
|
||||
|
||||
@Input() @Required() dossier: Dossier;
|
||||
@Input() @Required() selectedFiles: File[];
|
||||
analysisForced: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
@ -37,15 +32,10 @@ export class DossierOverviewBulkActionsComponent {
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _translateService: TranslateService,
|
||||
readonly listingService: ListingService<File>,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _filesService: FilesService,
|
||||
) {}
|
||||
|
||||
get selectedFiles(): File[] {
|
||||
return this.listingService.selected;
|
||||
}
|
||||
|
||||
get allSelectedFilesCanBeAssignedIntoSameState() {
|
||||
const allFilesAreUnderReviewOrUnassigned = this.selectedFiles.reduce(
|
||||
(acc, file) => acc && (file.isUnderReview || file.isUnassigned),
|
||||
@ -131,51 +121,58 @@ export class DossierOverviewBulkActionsComponent {
|
||||
this.dossier.dossierId,
|
||||
)
|
||||
.toPromise();
|
||||
this.reload.emit();
|
||||
this._loadingService.stop();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setToUnderApproval() {
|
||||
async setToUnderApproval() {
|
||||
// If more than 1 approver - show dialog and ask who to assign
|
||||
if (this.dossier.approverIds.length > 1) {
|
||||
this._assignFiles('approver', true);
|
||||
} else {
|
||||
this._performBulkAction(
|
||||
this._filesService.setUnderApprovalFor(
|
||||
this._loadingService.start();
|
||||
await this._filesService
|
||||
.setUnderApprovalFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
this.dossier.approverIds[0],
|
||||
),
|
||||
);
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
reanalyse() {
|
||||
async reanalyse() {
|
||||
this._loadingService.start();
|
||||
const fileIds = this.selectedFiles.filter(file => file.analysisRequired).map(file => file.fileId);
|
||||
this._performBulkAction(this._reanalysisService.reanalyzeFilesForDossier(fileIds, this.dossier.id));
|
||||
await this._reanalysisService.reanalyzeFilesForDossier(fileIds, this.dossier.id).toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
ocr() {
|
||||
this._performBulkAction(
|
||||
this._reanalysisService.ocrFiles(
|
||||
async ocr() {
|
||||
this._loadingService.start();
|
||||
await this._reanalysisService
|
||||
.ocrFiles(
|
||||
this.selectedFiles.map(f => f.fileId),
|
||||
this.dossier.id,
|
||||
),
|
||||
);
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
setToUnderReview() {
|
||||
this._performBulkAction(
|
||||
this._filesService.setUnderReviewFor(
|
||||
async setToUnderReview() {
|
||||
this._loadingService.start();
|
||||
await this._filesService
|
||||
.setUnderReviewFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
),
|
||||
);
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
approveDocuments() {
|
||||
async approveDocuments(): Promise<void> {
|
||||
const foundUpdatedFile = this.selectedFiles.find(file => file.hasUpdates);
|
||||
if (foundUpdatedFile) {
|
||||
this._dialogService.openDialog(
|
||||
@ -185,31 +182,31 @@ export class DossierOverviewBulkActionsComponent {
|
||||
title: _('confirmation-dialog.approve-multiple-files.title'),
|
||||
question: _('confirmation-dialog.approve-multiple-files.question'),
|
||||
}),
|
||||
() => {
|
||||
this._performBulkAction(
|
||||
this._filesService.setApprovedFor(
|
||||
async () => {
|
||||
this._loadingService.start();
|
||||
await this._filesService
|
||||
.setApprovedFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
),
|
||||
);
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this._performBulkAction(
|
||||
this._filesService.setApprovedFor(
|
||||
this._loadingService.start();
|
||||
await this._filesService
|
||||
.setApprovedFor(
|
||||
this.selectedFiles.map(f => f.id),
|
||||
this.dossier.id,
|
||||
),
|
||||
);
|
||||
)
|
||||
.toPromise();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
assignToMe() {
|
||||
this._fileAssignService.assignToMe(this.selectedFiles).then(() => {
|
||||
this._loadingService.start();
|
||||
this.reload.emit();
|
||||
this._loadingService.stop();
|
||||
});
|
||||
async assignToMe() {
|
||||
await this._fileAssignService.assignToMe(this.selectedFiles);
|
||||
}
|
||||
|
||||
assign() {
|
||||
@ -219,18 +216,6 @@ export class DossierOverviewBulkActionsComponent {
|
||||
|
||||
private _assignFiles(mode: 'reviewer' | 'approver', ignoreChanged = false) {
|
||||
const data = { mode, files: this.selectedFiles, ignoreChanged };
|
||||
this._dialogService.openDialog('assignFile', null, data, () => {
|
||||
this._loadingService.start();
|
||||
this.reload.emit();
|
||||
this._loadingService.stop();
|
||||
});
|
||||
}
|
||||
|
||||
private _performBulkAction(obs: Observable<any>) {
|
||||
this._loadingService.start();
|
||||
obs.subscribe().add(() => {
|
||||
this.reload.emit();
|
||||
this._loadingService.stop();
|
||||
});
|
||||
this._dialogService.openDialog('assignFile', null, data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { ActionConfig, CircleButtonTypes, EntitiesService, List, ListingService, SortingService, Toaster } from '@iqser/common-ui';
|
||||
import {
|
||||
ActionConfig,
|
||||
CircleButtonTypes,
|
||||
EntitiesService,
|
||||
List,
|
||||
ListingService,
|
||||
LoadingService,
|
||||
SortingService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
@ -34,6 +43,7 @@ export class ScreenHeaderComponent implements OnInit {
|
||||
readonly entitiesService: EntitiesService<File>,
|
||||
readonly routerHistoryService: RouterHistoryService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -41,13 +51,14 @@ export class ScreenHeaderComponent implements OnInit {
|
||||
}
|
||||
|
||||
async reanalyseDossier() {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
await this._reanalysisService.reanalyzeDossier(this.dossier.dossierId, true).toPromise();
|
||||
this.actionPerformed.emit('reload');
|
||||
this._toaster.success(_('dossier-overview.reanalyse-dossier.success'));
|
||||
} catch (e) {
|
||||
this._toaster.error(_('dossier-overview.reanalyse-dossier.error'));
|
||||
}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
exportFilesAsCSV() {
|
||||
|
||||
@ -55,7 +55,6 @@
|
||||
</div>
|
||||
|
||||
<redaction-file-actions
|
||||
(actionPerformed)="calculateData.emit($event)"
|
||||
*ngIf="!file.isProcessing"
|
||||
[file]="file"
|
||||
class="mr-4"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { File, IFileAttributeConfig } from '@red/domain';
|
||||
import { Required } from '@iqser/common-ui';
|
||||
|
||||
@ -12,5 +12,4 @@ export class TableItemComponent {
|
||||
@Input() @Required() file!: File;
|
||||
@Input() @Required() displayedAttributes!: IFileAttributeConfig[];
|
||||
@Input() dossierTemplateId: string;
|
||||
@Output() readonly calculateData = new EventEmitter<string>();
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@
|
||||
</div>
|
||||
|
||||
<redaction-file-actions
|
||||
(actionPerformed)="actionPerformed.emit($event)"
|
||||
*ngIf="!file.isProcessing"
|
||||
[file]="file"
|
||||
type="dossier-overview-workflow"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { File } from '@red/domain';
|
||||
|
||||
@Component({
|
||||
@ -9,5 +9,4 @@ import { File } from '@red/domain';
|
||||
})
|
||||
export class WorkflowItemComponent {
|
||||
@Input() file: File;
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
}
|
||||
|
||||
@ -15,7 +15,6 @@ import {
|
||||
import { File, IFileAttributeConfig, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain';
|
||||
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { FileAssignService } from '../../shared/services/file-assign.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -38,7 +37,6 @@ export class ConfigService {
|
||||
private readonly _fileAssignService: FileAssignService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _translateService: TranslateService,
|
||||
@ -57,6 +55,47 @@ export class ConfigService {
|
||||
this._listingMode$.next(listingMode);
|
||||
}
|
||||
|
||||
get workflowConfig(): WorkflowConfig<File, WorkflowFileStatus> {
|
||||
return {
|
||||
columnIdentifierFn: entity => entity.workflowStatus,
|
||||
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
|
||||
columns: [
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNASSIGNED],
|
||||
key: WorkflowFileStatuses.UNASSIGNED,
|
||||
enterFn: this._unassignFn,
|
||||
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
|
||||
color: '#D3D5DA',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
|
||||
enterFn: this._underReviewFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderReview(file) ||
|
||||
this._permissionsService.canAssignToSelf(file) ||
|
||||
this._permissionsService.canAssignUser(file),
|
||||
key: WorkflowFileStatuses.UNDER_REVIEW,
|
||||
color: '#FDBD00',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
|
||||
enterFn: this._underApprovalFn,
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file),
|
||||
key: WorkflowFileStatuses.UNDER_APPROVAL,
|
||||
color: '#374C81',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
|
||||
enterFn: this._approveFn,
|
||||
enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved,
|
||||
key: WorkflowFileStatuses.APPROVED,
|
||||
color: '#48C9F7',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
actionConfig(dossierId: string): List<ActionConfig> {
|
||||
return [
|
||||
{
|
||||
@ -106,47 +145,6 @@ export class ConfigService {
|
||||
];
|
||||
}
|
||||
|
||||
workflowConfig(reloadDossiers: () => Promise<void>): WorkflowConfig<File, WorkflowFileStatus> {
|
||||
return {
|
||||
columnIdentifierFn: entity => entity.workflowStatus,
|
||||
itemVersionFn: (entity: File) => `${entity.lastUpdated}-${entity.numberOfAnalyses}`,
|
||||
columns: [
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNASSIGNED],
|
||||
key: WorkflowFileStatuses.UNASSIGNED,
|
||||
enterFn: this._unassignFn(reloadDossiers),
|
||||
enterPredicate: (file: File) => this._permissionsService.canUnassignUser(file),
|
||||
color: '#D3D5DA',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_REVIEW],
|
||||
enterFn: this._underReviewFn(reloadDossiers),
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderReview(file) ||
|
||||
this._permissionsService.canAssignToSelf(file) ||
|
||||
this._permissionsService.canAssignUser(file),
|
||||
key: WorkflowFileStatuses.UNDER_REVIEW,
|
||||
color: '#FDBD00',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.UNDER_APPROVAL],
|
||||
enterFn: this._underApprovalFn(reloadDossiers),
|
||||
enterPredicate: (file: File) =>
|
||||
this._permissionsService.canSetUnderApproval(file) || this._permissionsService.canUndoApproval(file),
|
||||
key: WorkflowFileStatuses.UNDER_APPROVAL,
|
||||
color: '#374C81',
|
||||
},
|
||||
{
|
||||
label: workflowFileStatusTranslations[WorkflowFileStatuses.APPROVED],
|
||||
enterFn: this._approveFn(reloadDossiers),
|
||||
enterPredicate: (file: File) => this._permissionsService.isReadyForApproval(file) && file.canBeApproved,
|
||||
key: WorkflowFileStatuses.APPROVED,
|
||||
color: '#48C9F7',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
filterGroups(
|
||||
entities: File[],
|
||||
fileAttributeConfigs: IFileAttributeConfig[],
|
||||
@ -341,7 +339,7 @@ export class ConfigService {
|
||||
{
|
||||
id: 'assigned-to-me',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
|
||||
checker: this._recentlyModifiedChecker,
|
||||
checker: this._assignedToMeChecker,
|
||||
disabled: entities.filter(this._assignedToMeChecker).length === 0,
|
||||
},
|
||||
{
|
||||
@ -363,36 +361,34 @@ export class ConfigService {
|
||||
this._dialogService.openDialog('editDossier', $event, { dossierId });
|
||||
}
|
||||
|
||||
private _unassignFn = (reloadDossiers: () => Promise<void>) => async (file: File) => {
|
||||
private _unassignFn = async (file: File) => {
|
||||
this._loadingService.start();
|
||||
if (file.isUnderReview) {
|
||||
await this._filesService.setReviewerFor([file.fileId], file.dossierId, null).toPromise();
|
||||
} else if (file.isUnderApproval) {
|
||||
await this._filesService.setUnderApprovalFor([file.fileId], file.dossierId, null).toPromise();
|
||||
}
|
||||
this._loadingService.loadWhile(reloadDossiers());
|
||||
this._loadingService.stop();
|
||||
};
|
||||
|
||||
private _underReviewFn = (reloadDossiers: () => Promise<void>) => (file: File) => {
|
||||
this._fileAssignService.assignReviewer(null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
private _underReviewFn = async (file: File) => {
|
||||
await this._fileAssignService.assignReviewer(null, file, true);
|
||||
};
|
||||
|
||||
private _underApprovalFn = (reloadDossiers: () => Promise<void>) => async (file: File) => {
|
||||
private _underApprovalFn = async (file: File) => {
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
if (dossier.approverIds.length > 1) {
|
||||
this._fileAssignService.assignApprover(null, file, () => this._loadingService.loadWhile(reloadDossiers()), true);
|
||||
await this._fileAssignService.assignApprover(null, file, true);
|
||||
} else {
|
||||
this._loadingService.start();
|
||||
await this._filesService.setUnderApprovalFor([file.id], dossier.dossierId, dossier.approverIds[0]).toPromise();
|
||||
await reloadDossiers();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
};
|
||||
|
||||
private _approveFn = (reloadDossiers: () => Promise<void>) => async (file: File) => {
|
||||
private _approveFn = async (file: File) => {
|
||||
this._loadingService.start();
|
||||
await this._filesService.setApprovedFor([file.id], file.dossierId).toPromise();
|
||||
await reloadDossiers();
|
||||
this._loadingService.stop();
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
<ng-container *ngIf="dossier$ | async as dossier">
|
||||
<section (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||
<redaction-screen-header
|
||||
(actionPerformed)="actionPerformed($event)"
|
||||
[analysisForced]="analysisForced"
|
||||
[dossier]="dossier"
|
||||
></redaction-screen-header>
|
||||
@ -54,12 +53,12 @@
|
||||
</section>
|
||||
|
||||
<ng-template #bulkActions>
|
||||
<redaction-dossier-overview-bulk-actions (reload)="reloadFiles()" [dossier]="dossier"></redaction-dossier-overview-bulk-actions>
|
||||
<redaction-dossier-overview-bulk-actions [dossier]="dossier"
|
||||
[selectedFiles]="this.listingService.selected"></redaction-dossier-overview-bulk-actions>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #tableItemTemplate let-file="entity">
|
||||
<redaction-table-item
|
||||
(calculateData)="actionPerformed($event)"
|
||||
[displayedAttributes]="displayedAttributes"
|
||||
[dossierTemplateId]="dossier.dossierTemplateId"
|
||||
[file]="file"
|
||||
@ -74,5 +73,5 @@
|
||||
<input #fileInput (change)="uploadFiles($event.target['files'])" class="file-upload-input" multiple="true" type="file" />
|
||||
|
||||
<ng-template #workflowItemTemplate let-entity="entity">
|
||||
<redaction-workflow-item (actionPerformed)="actionPerformed($event, entity)" [file]="entity"></redaction-workflow-item>
|
||||
<redaction-workflow-item [file]="entity"></redaction-workflow-item>
|
||||
</ng-template>
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Dossier, DossierAttributeWithValue, File, IFileAttributeConfig, WorkflowFileStatus } from '@red/domain';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
||||
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
||||
@ -65,7 +64,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
analysisForced: boolean;
|
||||
displayedInFileListAttributes: IFileAttributeConfig[] = [];
|
||||
displayedAttributes: IFileAttributeConfig[] = [];
|
||||
readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus> = this.configService.workflowConfig(() => this.reloadFiles());
|
||||
readonly workflowConfig: WorkflowConfig<File, WorkflowFileStatus> = this.configService.workflowConfig;
|
||||
readonly dossier$: Observable<Dossier>;
|
||||
readonly dossierId: string;
|
||||
currentDossier: Dossier;
|
||||
@ -80,7 +79,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
private readonly _router: Router,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _appConfigService: AppConfigService,
|
||||
@ -124,22 +122,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
return this.filterService.getGroup('quickFilters')?.filters.filter(f => !f.required && f.checked);
|
||||
}
|
||||
|
||||
async actionPerformed(action?: string, file?: File) {
|
||||
if (['assign-reviewer', 'reload'].includes(action)) {
|
||||
return this.reloadFiles();
|
||||
}
|
||||
|
||||
if (action === 'upload') {
|
||||
return this._fileInput.nativeElement.click();
|
||||
}
|
||||
|
||||
this._loadEntitiesFromState();
|
||||
|
||||
if (action === 'navigate') {
|
||||
await this._router.navigate([file.routerLink]);
|
||||
}
|
||||
}
|
||||
|
||||
disabledFn = (file: File) => file.excluded;
|
||||
|
||||
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
|
||||
@ -161,7 +143,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
.pipe(
|
||||
switchMap(() => this._filesService.hasChanges$(this.dossierId)),
|
||||
filter(changed => changed),
|
||||
switchMap(() => this.reloadFiles()),
|
||||
switchMap(() => this._reloadFiles()),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
@ -202,11 +184,6 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
|
||||
}
|
||||
|
||||
async reloadFiles() {
|
||||
await this._filesService.loadAll(this.dossierId).toPromise();
|
||||
this._computeAllFilters();
|
||||
}
|
||||
|
||||
@HostListener('drop', ['$event'])
|
||||
onDrop(event: DragEvent): void {
|
||||
const currentDossier = this._dossiersService.find(this.dossierId);
|
||||
@ -227,6 +204,11 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
|
||||
recentlyModifiedChecker = (file: File) =>
|
||||
moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
|
||||
|
||||
private async _reloadFiles() {
|
||||
await this._filesService.loadAll(this.dossierId).toPromise();
|
||||
this._computeAllFilters();
|
||||
}
|
||||
|
||||
private _loadEntitiesFromState() {
|
||||
this.currentDossier = this._dossiersService.find(this.dossierId);
|
||||
this._computeAllFilters();
|
||||
|
||||
@ -8,7 +8,6 @@ import { LongPressEvent } from '@shared/directives/long-press.directive';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { switchMapTo, tap } from 'rxjs/operators';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
|
||||
@Component({
|
||||
@ -66,7 +65,6 @@ export class DossiersListingActionsComponent implements OnChanges {
|
||||
|
||||
async reanalyseDossier($event: MouseEvent, id: string): Promise<void> {
|
||||
$event.stopPropagation();
|
||||
const reanalysis$ = this._reanalysisService.reanalyzeDossier(id).pipe(switchMapTo(this._dossiersService.loadAll()));
|
||||
await reanalysis$.pipe(tap(() => this.actionPerformed.emit())).toPromise();
|
||||
await this._reanalysisService.reanalyzeDossier(id).toPromise();
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import { UserService } from '@services/user.service';
|
||||
import { workflowFileStatusTranslations } from '../../translations/file-status-translations';
|
||||
import { dossierMemberChecker, dossierTemplateChecker, RedactionFilterSorter } from '@utils/index';
|
||||
import { workloadTranslations } from '../../translations/workload-translations';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
|
||||
|
||||
@ -18,7 +17,6 @@ export class ConfigService {
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dossierStatsService: DossierStatsService,
|
||||
) {}
|
||||
@ -44,38 +42,6 @@ export class ConfigService {
|
||||
|
||||
_otherChecker = (dw: Dossier) => !dw.memberIds.includes(this._currentUser.id);
|
||||
|
||||
private _quickFilters(entities: Dossier[]): NestedFilter[] {
|
||||
const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers');
|
||||
const filters = [
|
||||
{
|
||||
id: 'my-dossiers',
|
||||
label: myDossiersLabel,
|
||||
checker: this._myDossiersChecker,
|
||||
disabled: entities.filter(this._myDossiersChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'to-approve',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
|
||||
checker: this._toApproveChecker,
|
||||
disabled: entities.filter(this._toApproveChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'to-review',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
|
||||
checker: this._toReviewChecker,
|
||||
disabled: entities.filter(this._toReviewChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.other'),
|
||||
checker: this._otherChecker,
|
||||
disabled: entities.filter(this._otherChecker).length === 0,
|
||||
},
|
||||
].map(filter => new NestedFilter(filter));
|
||||
|
||||
return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled);
|
||||
}
|
||||
|
||||
buttonsConfig(addDossier: () => void): ButtonConfig[] {
|
||||
return [
|
||||
{
|
||||
@ -214,6 +180,38 @@ export class ConfigService {
|
||||
return filterGroups;
|
||||
}
|
||||
|
||||
private _quickFilters(entities: Dossier[]): NestedFilter[] {
|
||||
const myDossiersLabel = this._translateService.instant('dossier-listing.quick-filters.my-dossiers');
|
||||
const filters = [
|
||||
{
|
||||
id: 'my-dossiers',
|
||||
label: myDossiersLabel,
|
||||
checker: this._myDossiersChecker,
|
||||
disabled: entities.filter(this._myDossiersChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'to-approve',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-approve'),
|
||||
checker: this._toApproveChecker,
|
||||
disabled: entities.filter(this._toApproveChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'to-review',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.to-review'),
|
||||
checker: this._toReviewChecker,
|
||||
disabled: entities.filter(this._toReviewChecker).length === 0,
|
||||
},
|
||||
{
|
||||
id: 'other',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.other'),
|
||||
checker: this._otherChecker,
|
||||
disabled: entities.filter(this._otherChecker).length === 0,
|
||||
},
|
||||
].map(filter => new NestedFilter(filter));
|
||||
|
||||
return filters.filter(f => f.label === myDossiersLabel || this._userPreferenceService.areDevFeaturesEnabled);
|
||||
}
|
||||
|
||||
private _dossierStatusChecker = (dossier: Dossier, filter: INestedFilter) => {
|
||||
const stats = this._dossierStatsService.get(dossier.dossierId);
|
||||
return stats?.fileCountPerWorkflowStatus[filter.id];
|
||||
|
||||
@ -93,9 +93,11 @@
|
||||
|
||||
<redaction-file-actions
|
||||
#fileActions
|
||||
(actionPerformed)="fileActionPerformed($event)"
|
||||
(ocredFile)="ocredFile()"
|
||||
(toggleViewDocumentInfo)="toggleViewDocumentInfo()"
|
||||
(toggleViewExcludedPages)="toggleViewExcludedPages()"
|
||||
[activeDocumentInfo]="viewDocumentInfo"
|
||||
[activeExcludePages]="excludePages"
|
||||
[activeExcludePages]="showExcludedPages"
|
||||
[file]="file"
|
||||
type="file-preview"
|
||||
></redaction-file-actions>
|
||||
@ -156,36 +158,38 @@
|
||||
|
||||
<div class="right-container">
|
||||
<iqser-empty-state
|
||||
*ngIf="file.excluded && !viewDocumentInfo && !excludePages"
|
||||
*ngIf="file.excluded && !viewDocumentInfo && !showExcludedPages"
|
||||
[horizontalPadding]="40"
|
||||
[text]="'file-preview.tabs.is-excluded' | translate"
|
||||
icon="red:needs-work"
|
||||
></iqser-empty-state>
|
||||
|
||||
<redaction-document-info
|
||||
(closeDocumentInfoView)="viewDocumentInfo = false"
|
||||
(closeDocumentInfoView)="toggleViewDocumentInfo()"
|
||||
*ngIf="viewDocumentInfo"
|
||||
[file]="fileData.file"
|
||||
></redaction-document-info>
|
||||
|
||||
<redaction-file-workload
|
||||
#fileWorkloadComponent
|
||||
(actionPerformed)="fileActionPerformed($event)"
|
||||
(annotationsChanged)="annotationsChangedByReviewAction($event)"
|
||||
(deselectAnnotations)="deselectAnnotations($event)"
|
||||
(excludePages)="excludePages()"
|
||||
(selectAnnotations)="selectAnnotations($event)"
|
||||
(selectPage)="selectPage($event)"
|
||||
(toggleSkipped)="toggleSkipped($event)"
|
||||
(toggleViewExcludedPages)="toggleViewExcludedPages()"
|
||||
*ngIf="!file.excluded"
|
||||
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="annotations"
|
||||
[dialogRef]="dialogRef"
|
||||
[excludePages]="excludePages"
|
||||
[dossier]="dossier"
|
||||
[file]="file"
|
||||
[hideSkipped]="hideSkipped"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[showExcludedPages]="showExcludedPages"
|
||||
[viewedPages]="fileData?.viewedPages"
|
||||
[viewer]="activeViewer"
|
||||
></redaction-file-workload>
|
||||
|
||||
@ -81,7 +81,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
hideSkipped = false;
|
||||
displayPDFViewer = false;
|
||||
viewDocumentInfo = false;
|
||||
excludePages = false;
|
||||
showExcludedPages = false;
|
||||
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('fileActions') fileActions: FileActionsComponent;
|
||||
readonly dossierId: string;
|
||||
@ -271,7 +271,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
if (file?.analysisRequired) {
|
||||
this.fileActions.reanalyseFile();
|
||||
await this.fileActions.reanalyseFile();
|
||||
}
|
||||
|
||||
this.displayPDFViewer = true;
|
||||
@ -367,7 +367,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
response.manualRedactionEntryWrapper.rectId,
|
||||
);
|
||||
this._instance.Core.annotationManager.deleteAnnotation(annotation);
|
||||
await this._filesService.reload(this.dossierId, this.fileId);
|
||||
await this._filesService.reload(this.dossierId, this.fileId).toPromise();
|
||||
const distinctPages = manualRedactionEntryWrapper.manualRedactionEntry.positions
|
||||
.map(p => p.page)
|
||||
.filter((item, pos, self) => self.indexOf(item) === pos);
|
||||
@ -459,58 +459,33 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
await this._cleanupAndRedrawManualAnnotationsForEntirePage(annotation?.pageNumber || this.activeViewerPage);
|
||||
}
|
||||
|
||||
async fileActionPerformed(action: string) {
|
||||
this.editingReviewer = false;
|
||||
ocredFile(): void {
|
||||
this._updateCanPerformActions();
|
||||
this._reloadFileOnReanalysis = true;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case 'enable-analysis':
|
||||
case 'disable-analysis':
|
||||
this._loadingService.start();
|
||||
// the trigger will disable it later
|
||||
break;
|
||||
async excludePages(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
await this._loadFileData(true);
|
||||
this._cleanupAndRedrawManualAnnotations$();
|
||||
await this._stampPDF();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
case 'delete':
|
||||
return this._router.navigate([this.dossiersService.find(this.dossierId).routerLink]);
|
||||
toggleViewExcludedPages(): void {
|
||||
this.showExcludedPages = !this.showExcludedPages;
|
||||
this._workloadComponent.multiSelectActive = false;
|
||||
this.viewDocumentInfo = false;
|
||||
}
|
||||
|
||||
case 'reanalyse':
|
||||
await this._loadFileData(true);
|
||||
this._updateCanPerformActions();
|
||||
await this._filesService.loadAll(this.dossierId).toPromise();
|
||||
return;
|
||||
|
||||
case 'exclude-pages':
|
||||
await this._filesService.loadAll(this.dossierId).toPromise();
|
||||
await this._loadFileData(true);
|
||||
this._cleanupAndRedrawManualAnnotations$();
|
||||
await this._stampPDF();
|
||||
this._loadingService.stop();
|
||||
return;
|
||||
|
||||
case 'view-document-info':
|
||||
this.viewDocumentInfo = !this.viewDocumentInfo;
|
||||
return;
|
||||
|
||||
case 'view-exclude-pages':
|
||||
this.excludePages = !this.excludePages;
|
||||
this._workloadComponent.multiSelectActive = false;
|
||||
this.viewDocumentInfo = false;
|
||||
return;
|
||||
|
||||
case 'ocr-file':
|
||||
this._updateCanPerformActions();
|
||||
this._reloadFileOnReanalysis = true;
|
||||
return;
|
||||
|
||||
default:
|
||||
this._updateCanPerformActions();
|
||||
}
|
||||
toggleViewDocumentInfo(): void {
|
||||
this.viewDocumentInfo = !this.viewDocumentInfo;
|
||||
this._workloadComponent.multiSelectActive = false;
|
||||
this.showExcludedPages = false;
|
||||
}
|
||||
|
||||
async assignToMe(file: File) {
|
||||
await this._fileAssignService.assignToMe([file], async () => {
|
||||
await this._filesService.reload(this.dossierId, this.fileId);
|
||||
this._updateCanPerformActions();
|
||||
});
|
||||
await this._fileAssignService.assignToMe([file]);
|
||||
}
|
||||
|
||||
async assignReviewer(file: File, user: User | string) {
|
||||
@ -518,11 +493,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
const reviewerName = this.userService.getNameForId(reviewerId);
|
||||
|
||||
const { dossierId, fileId, filename } = file;
|
||||
this._loadingService.start();
|
||||
await this._filesService.setReviewerFor([fileId], dossierId, reviewerId).toPromise();
|
||||
this._loadingService.stop();
|
||||
|
||||
this._toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } });
|
||||
await this._filesService.reload(this.dossierId, this.fileId);
|
||||
this._updateCanPerformActions();
|
||||
this.editingReviewer = false;
|
||||
}
|
||||
|
||||
@ -619,13 +594,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this.addSubscription = timer(0, 5000)
|
||||
.pipe(switchMap(() => this._filesService.reload(this.dossierId, this.fileId)))
|
||||
.subscribe();
|
||||
this.addSubscription = this.file$.subscribe(() => {
|
||||
this._updateCanPerformActions();
|
||||
});
|
||||
this.addSubscription = this._filesMapService.fileReanalysed$
|
||||
.pipe(filter(file => file.fileId === this.fileId))
|
||||
.subscribe(async () => {
|
||||
await this._loadFileData(!this._reloadFileOnReanalysis);
|
||||
this._reloadFileOnReanalysis = false;
|
||||
this._loadingService.stop();
|
||||
this._updateCanPerformActions();
|
||||
this._cleanupAndRedrawManualAnnotations$();
|
||||
});
|
||||
}
|
||||
@ -679,7 +656,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page);
|
||||
const currentPageAnnotationIds = currentPageAnnotations.map(a => a.id);
|
||||
|
||||
await this._filesService.reload(this.dossierId, this.fileId);
|
||||
await this._filesService.reload(this.dossierId, this.fileId).toPromise();
|
||||
this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
|
||||
|
||||
this.rebuildFilters();
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
></redaction-file-download-btn>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleViewDocumentInfo()"
|
||||
(action)="toggleViewDocumentInfo.emit()"
|
||||
*ngIf="showDocumentInfo"
|
||||
[attr.aria-expanded]="activeDocumentInfo"
|
||||
[tooltip]="'file-preview.document-info' | translate"
|
||||
@ -71,7 +71,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleExcludePages()"
|
||||
(action)="toggleViewExcludedPages.emit()"
|
||||
*ngIf="showExcludePages"
|
||||
[attr.aria-expanded]="activeExcludePages"
|
||||
[showDot]="!!file.excludedPages?.length"
|
||||
|
||||
@ -22,6 +22,7 @@ import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-actions',
|
||||
@ -37,7 +38,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
@Input() activeDocumentInfo: boolean;
|
||||
@Input() activeExcludePages: boolean;
|
||||
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
@Output() readonly ocredFile = new EventEmitter<void>();
|
||||
|
||||
toggleTooltip?: string;
|
||||
assignTooltip?: string;
|
||||
@ -63,6 +64,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
isDossierOverviewWorkflow = false;
|
||||
isFilePreview = false;
|
||||
tooltipPosition: IqserTooltipPosition;
|
||||
@Output() readonly toggleViewDocumentInfo = new EventEmitter<void>();
|
||||
@Output() readonly toggleViewExcludedPages = new EventEmitter<void>();
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -77,6 +80,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _router: Router,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@ -93,16 +97,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
this.setup();
|
||||
}
|
||||
|
||||
toggleViewDocumentInfo() {
|
||||
this.actionPerformed.emit('view-document-info');
|
||||
}
|
||||
|
||||
toggleExcludePages() {
|
||||
this.actionPerformed.emit('view-exclude-pages');
|
||||
}
|
||||
|
||||
openDocument() {
|
||||
this.actionPerformed.emit('navigate');
|
||||
this._router.navigate([this.file.routerLink]).then();
|
||||
}
|
||||
|
||||
openDeleteFileDialog($event: MouseEvent) {
|
||||
@ -121,8 +117,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
.catch(error => {
|
||||
this._toaster.error(_('error.http.generic'), { params: error });
|
||||
});
|
||||
await this._filesService.loadAll(this.file.dossierId).toPromise();
|
||||
this.actionPerformed.emit('delete');
|
||||
this._loadingService.stop();
|
||||
},
|
||||
);
|
||||
@ -131,39 +125,24 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
assign($event: MouseEvent) {
|
||||
const mode = this.file.isUnderApproval ? 'approver' : 'reviewer';
|
||||
const files = [this.file];
|
||||
this._dialogService.openDialog('assignFile', $event, { mode, files }, () => {
|
||||
this.actionPerformed.emit('assign-reviewer');
|
||||
});
|
||||
this._dialogService.openDialog('assignFile', $event, { mode, files });
|
||||
}
|
||||
|
||||
async assignToMe($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
|
||||
await this._fileAssignService.assignToMe([this.file], () => {
|
||||
this.reloadFiles('reanalyse');
|
||||
});
|
||||
await this._fileAssignService.assignToMe([this.file]);
|
||||
}
|
||||
|
||||
reanalyseFile($event?: MouseEvent) {
|
||||
async reanalyseFile($event?: MouseEvent) {
|
||||
if ($event) {
|
||||
$event.stopPropagation();
|
||||
}
|
||||
this.addSubscription = this._reanalysisService
|
||||
.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true)
|
||||
.subscribe(() => {
|
||||
this.reloadFiles('reanalyse');
|
||||
});
|
||||
await this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true).toPromise();
|
||||
}
|
||||
|
||||
async setFileUnderApproval($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
const dossier = this.dossiersService.find(this.file.dossierId);
|
||||
if (dossier.approverIds.length > 1) {
|
||||
this._fileAssignService.assignApprover($event, this.file, () => this.reloadFiles('assign-reviewer'), true);
|
||||
} else {
|
||||
await this._filesService.setUnderApprovalFor([this.file.id], dossier.id, dossier.approverIds[0]).toPromise();
|
||||
this.reloadFiles('set-under-approval');
|
||||
}
|
||||
await this._fileAssignService.assignApprover($event, this.file, true);
|
||||
}
|
||||
|
||||
async setFileApproved($event: MouseEvent) {
|
||||
@ -188,27 +167,20 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
|
||||
async ocrFile($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this._loadingService.start();
|
||||
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
|
||||
this.reloadFiles('ocr-file');
|
||||
this.ocredFile.emit();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
||||
this._fileAssignService.assignReviewer($event, this.file, () => this.reloadFiles('assign-reviewer'), ignoreDialogChanges);
|
||||
}
|
||||
|
||||
reloadFiles(action: string) {
|
||||
this._filesService
|
||||
.loadAll(this.file.dossierId)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
this.actionPerformed.emit(action);
|
||||
});
|
||||
async setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
||||
await this._fileAssignService.assignReviewer($event, this.file, ignoreDialogChanges);
|
||||
}
|
||||
|
||||
async toggleAnalysis() {
|
||||
this._loadingService.start();
|
||||
await this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded).toPromise();
|
||||
await this._filesService.loadAll(this.file.dossierId).toPromise();
|
||||
this.actionPerformed.emit(this.file?.excluded ? 'enable-analysis' : 'disable-analysis');
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
forceReanalysisAction($event: LongPressEvent) {
|
||||
@ -216,9 +188,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
}
|
||||
|
||||
setup() {
|
||||
this.isDossierOverview = this.type.startsWith('dossier-overview-list');
|
||||
this.isDossierOverviewList = this.type === 'dossier-overview-list';
|
||||
this.isDossierOverviewWorkflow = this.type === 'dossier-overview-workflow';
|
||||
this.isDossierOverview = this.type.startsWith('dossier-overview');
|
||||
this.isFilePreview = this.type === 'file-preview';
|
||||
|
||||
this.tooltipPosition = this.isFilePreview ? 'below' : 'above';
|
||||
@ -251,7 +223,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
}
|
||||
|
||||
private async _setFileApproved() {
|
||||
this._loadingService.start();
|
||||
await this._filesService.setApprovedFor([this.file.id], this.file.dossierId).toPromise();
|
||||
this.reloadFiles('set-approved');
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,10 @@ import { Dossier, File } from '@red/domain';
|
||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { ConfirmationDialogInput, Toaster } from '@iqser/common-ui';
|
||||
import { ConfirmationDialogInput, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class FileAssignService {
|
||||
@ -14,10 +16,11 @@ export class FileAssignService {
|
||||
private readonly _userService: UserService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _toaster: Toaster,
|
||||
) {}
|
||||
|
||||
async assignToMe(files: File[], callback?: Function) {
|
||||
async assignToMe(files: File[]) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const atLeastOneFileHasReviewer = files.reduce((acc, fs) => acc || !!fs.currentReviewer, false);
|
||||
if (atLeastOneFileHasReviewer) {
|
||||
@ -26,48 +29,43 @@ export class FileAssignService {
|
||||
question: _('confirmation-dialog.assign-file-to-me.question'),
|
||||
});
|
||||
this._dialogService.openDialog('confirm', null, data, () => {
|
||||
this._assignReviewerToCurrentUser(files, callback)
|
||||
this._assignReviewerToCurrentUser(files)
|
||||
.toPromise()
|
||||
.then(() => resolve())
|
||||
.catch(() => reject());
|
||||
});
|
||||
} else {
|
||||
this._assignReviewerToCurrentUser(files, callback)
|
||||
this._assignReviewerToCurrentUser(files)
|
||||
.toPromise()
|
||||
.then(() => resolve())
|
||||
.catch(() => reject());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
assignReviewer($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
|
||||
this._assignFile('reviewer', $event, file, callback, ignoreChanged);
|
||||
async assignReviewer($event: MouseEvent, file: File, ignoreChanged = false): Promise<void> {
|
||||
await this._assignFile('reviewer', $event, file, ignoreChanged);
|
||||
}
|
||||
|
||||
assignApprover($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
|
||||
this._assignFile('approver', $event, file, callback, ignoreChanged);
|
||||
async assignApprover($event: MouseEvent, file: File, ignoreChanged = false): Promise<void> {
|
||||
await this._assignFile('approver', $event, file, ignoreChanged);
|
||||
}
|
||||
|
||||
private _assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, callback?: Function, ignoreChanged = false) {
|
||||
private async _assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, ignoreChanged = false): Promise<void> {
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
const userIds = this._getUserIds(mode, dossier);
|
||||
if (userIds.length === 1 || userIds.includes(this._userService.currentUser.id)) {
|
||||
if (userIds.length === 1) {
|
||||
$event?.stopPropagation(); // event$ is null when called from workflow view
|
||||
const userId = userIds.length === 1 ? userIds[0] : this._userService.currentUser.id;
|
||||
this._makeAssignFileRequest(userId, mode, [file]).then(async () => {
|
||||
if (callback) {
|
||||
await callback();
|
||||
}
|
||||
});
|
||||
await this._makeAssignFileRequest(userId, mode, [file]);
|
||||
} else {
|
||||
const data = { mode, files: [file], ignoreChanged };
|
||||
this._dialogService.openDialog('assignFile', $event, data, async () => {
|
||||
if (callback) {
|
||||
await callback();
|
||||
}
|
||||
});
|
||||
this._dialogService.openDialog('assignFile', $event, data);
|
||||
}
|
||||
}
|
||||
|
||||
private async _makeAssignFileRequest(userId: string, mode: 'reviewer' | 'approver', files: File[]) {
|
||||
this._loadingService.start();
|
||||
try {
|
||||
if (mode === 'reviewer') {
|
||||
await this._filesService
|
||||
@ -89,22 +87,21 @@ export class FileAssignService {
|
||||
} catch (error) {
|
||||
this._toaster.error(_('error.http.generic'), { params: error });
|
||||
}
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
private _getUserIds(mode: 'reviewer' | 'approver', dossier: Dossier) {
|
||||
return mode === 'approver' ? dossier.approverIds : dossier.memberIds;
|
||||
}
|
||||
|
||||
private async _assignReviewerToCurrentUser(files: File[], callback?: Function) {
|
||||
await this._filesService
|
||||
private _assignReviewerToCurrentUser(files: File[]): Observable<any> {
|
||||
this._loadingService.start();
|
||||
return this._filesService
|
||||
.setReviewerFor(
|
||||
files.map(f => f.fileId),
|
||||
files[0].dossierId,
|
||||
this._userService.currentUser.id,
|
||||
)
|
||||
.toPromise();
|
||||
if (callback) {
|
||||
await callback();
|
||||
}
|
||||
.pipe(tap(() => this._loadingService.stop()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,23 +37,36 @@ export class FilesMapService {
|
||||
return entities.forEach(entity => this._entityChanged$.next(entity));
|
||||
}
|
||||
|
||||
const reanalysedEntities = [];
|
||||
const changedEntities = [];
|
||||
|
||||
// Keep old object references for unchanged entities
|
||||
const newEntities = entities.map(newEntity => {
|
||||
const oldEntity = this.get(key, newEntity.id);
|
||||
|
||||
if (oldEntity?.lastProcessed !== newEntity.lastProcessed) {
|
||||
this.fileReanalysed$.next(newEntity);
|
||||
reanalysedEntities.push(newEntity);
|
||||
}
|
||||
|
||||
if (newEntity.isEqual(oldEntity)) {
|
||||
return oldEntity;
|
||||
}
|
||||
|
||||
this._entityChanged$.next(newEntity);
|
||||
changedEntities.push(newEntity);
|
||||
return newEntity;
|
||||
});
|
||||
|
||||
this._map.get(key).next(newEntities);
|
||||
|
||||
// Emit observables only after entities have been updated
|
||||
|
||||
for (const file of reanalysedEntities) {
|
||||
this.fileReanalysed$.next(file);
|
||||
}
|
||||
|
||||
for (const file of changedEntities) {
|
||||
this._entityChanged$.next(file);
|
||||
}
|
||||
}
|
||||
|
||||
replace(entity: File) {
|
||||
|
||||
@ -7,7 +7,6 @@ import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { map, mapTo, switchMap, tap } from 'rxjs/operators';
|
||||
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
|
||||
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
|
||||
@ -14,22 +14,22 @@ export class ReanalysisService extends GenericService<unknown> {
|
||||
|
||||
@Validate()
|
||||
excludePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
return this._post(body, `exclude-pages/${dossierId}/${fileId}`);
|
||||
return this._post(body, `exclude-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
includePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
return this._post(body, `include-pages/${dossierId}/${fileId}`);
|
||||
return this._post(body, `include-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
reanalyzeFilesForDossier(@RequiredParam() body: List, @RequiredParam() dossierId: string, force?: boolean) {
|
||||
reanalyzeFilesForDossier(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, force?: boolean) {
|
||||
const queryParams: QueryParam[] = [];
|
||||
if (force) {
|
||||
queryParams.push({ key: 'force', value: force });
|
||||
}
|
||||
|
||||
return this._post(body, `reanalyze/${dossierId}/bulk`, queryParams);
|
||||
return this._post(fileIds, `reanalyze/${dossierId}/bulk`, queryParams).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
|
||||
}
|
||||
|
||||
@Validate()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user