From dd319dfee9570c0cbd938179b18f5bd164b848a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Mon, 22 Nov 2021 21:11:53 +0200 Subject: [PATCH] Refactored file actions & event emitters --- .../file-workload.component.html | 19 ++-- .../file-workload/file-workload.component.ts | 9 +- .../page-exclusion.component.ts | 6 +- ...sign-reviewer-approver-dialog.component.ts | 67 +++++------ ...ssier-overview-bulk-actions.component.html | 2 +- ...dossier-overview-bulk-actions.component.ts | 107 ++++++++---------- .../screen-header/screen-header.component.ts | 15 ++- .../table-item/table-item.component.html | 1 - .../table-item/table-item.component.ts | 3 +- .../workflow-item.component.html | 1 - .../workflow-item/workflow-item.component.ts | 3 +- .../dossier-overview/config.service.ts | 102 ++++++++--------- .../dossier-overview-screen.component.html | 7 +- .../dossier-overview-screen.component.ts | 32 ++---- .../dossiers-listing-actions.component.ts | 4 +- .../dossiers-listing/config.service.ts | 66 ++++++----- .../file-preview-screen.component.html | 16 ++- .../file-preview-screen.component.ts | 83 +++++--------- .../file-actions/file-actions.component.html | 4 +- .../file-actions/file-actions.component.ts | 69 ++++------- .../shared/services/file-assign.service.ts | 49 ++++---- .../entity-services/files-map.service.ts | 17 ++- .../services/entity-services/files.service.ts | 1 - .../src/app/services/reanalysis.service.ts | 8 +- 24 files changed, 309 insertions(+), 382 deletions(-) diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html index 79d5cc468..ec54705ba 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html @@ -1,11 +1,11 @@ -
+
-
+
- +
{{ 'file-preview.tabs.annotations.page-is' | translate }} - . @@ -201,8 +202,8 @@
diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts index ea23c01ed..1741d5210 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts @@ -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; @Input() viewer: WebViewerInstance; @Output() readonly shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter(); @@ -54,10 +54,11 @@ export class FileWorkloadComponent { @Output() readonly selectPage = new EventEmitter(); @Output() readonly toggleSkipped = new EventEmitter(); @Output() readonly annotationsChanged = new EventEmitter(); - @Output() readonly actionPerformed = new EventEmitter(); + @Output() readonly excludePages = new EventEmitter(); displayedPages: number[] = []; pagesPanelActive = true; readonly displayedAnnotations$: Observable>; + @Output() @Required() readonly toggleViewExcludedPages = new EventEmitter(); private _annotations$ = new BehaviorSubject([]); @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[], diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts index 122980e03..81df44ccb 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts @@ -12,7 +12,7 @@ import { ReanalysisService } from '@services/reanalysis.service'; }) export class PageExclusionComponent implements OnChanges { @Input() file: File; - @Output() readonly actionPerformed = new EventEmitter(); + @Output() readonly excludePages = new EventEmitter(); 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(); } } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts index b69d7b02e..f230cdcb5 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts @@ -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, @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 diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html index 0dd90ce37..73b1f5890 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.html @@ -1,4 +1,4 @@ - + , 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 { 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) { - this._loadingService.start(); - obs.subscribe().add(() => { - this.reload.emit(); - this._loadingService.stop(); - }); + this._dialogService.openDialog('assignFile', null, data); } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/screen-header.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/screen-header.component.ts index c31056276..193eaefc8 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/screen-header.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/screen-header.component.ts @@ -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, 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() { diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html index a0a4be767..d2c68411b 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/table-item/table-item.component.html @@ -55,7 +55,6 @@
(); } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/workflow-item/workflow-item.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/workflow-item/workflow-item.component.html index 6148644a7..aabcddc26 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/workflow-item/workflow-item.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/workflow-item/workflow-item.component.html @@ -14,7 +14,6 @@
(); } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts index 2b429730d..6e30166ab 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/config.service.ts @@ -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 { + 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 { return [ { @@ -106,47 +145,6 @@ export class ConfigService { ]; } - workflowConfig(reloadDossiers: () => Promise): WorkflowConfig { - 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) => 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) => (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) => 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) => 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(); }; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html index 973b7739c..3c989493d 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html @@ -1,7 +1,6 @@
@@ -54,12 +53,12 @@
- + - + diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts index 724fbb019..d5e5ebcb4 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts @@ -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 imple analysisForced: boolean; displayedInFileListAttributes: IFileAttributeConfig[] = []; displayedAttributes: IFileAttributeConfig[] = []; - readonly workflowConfig: WorkflowConfig = this.configService.workflowConfig(() => this.reloadFiles()); + readonly workflowConfig: WorkflowConfig = this.configService.workflowConfig; readonly dossier$: Observable; readonly dossierId: string; currentDossier: Dossier; @@ -80,7 +79,6 @@ export class DossierOverviewScreenComponent extends ListingComponent 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 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 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 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 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(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts index 611d05fdb..6b347473f 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/components/dossiers-listing-actions/dossiers-listing-actions.component.ts @@ -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 { $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(); } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts index d78c92cfc..ce0711162 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/config.service.ts @@ -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]; diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html index f8724f975..99bb129ec 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html @@ -93,9 +93,11 @@ @@ -156,36 +158,38 @@
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts index f27fac5b5..2844c1ca0 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts @@ -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 { + 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(); diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html index 7955ceb8f..3b7168251 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html @@ -62,7 +62,7 @@ > (); + @Output() readonly ocredFile = new EventEmitter(); 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(); + @Output() readonly toggleViewExcludedPages = new EventEmitter(); 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(); } } diff --git a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts index f3329db62..af171b965 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts @@ -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((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 { + 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 { + 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 { 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 { + 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())); } } diff --git a/apps/red-ui/src/app/services/entity-services/files-map.service.ts b/apps/red-ui/src/app/services/entity-services/files-map.service.ts index 1402a468a..a8f69b6a4 100644 --- a/apps/red-ui/src/app/services/entity-services/files-map.service.ts +++ b/apps/red-ui/src/app/services/entity-services/files-map.service.ts @@ -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) { diff --git a/apps/red-ui/src/app/services/entity-services/files.service.ts b/apps/red-ui/src/app/services/entity-services/files.service.ts index 24ae31d60..da502bef2 100644 --- a/apps/red-ui/src/app/services/entity-services/files.service.ts +++ b/apps/red-ui/src/app/services/entity-services/files.service.ts @@ -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', }) diff --git a/apps/red-ui/src/app/services/reanalysis.service.ts b/apps/red-ui/src/app/services/reanalysis.service.ts index 1defc5008..fbe5c1b5e 100644 --- a/apps/red-ui/src/app/services/reanalysis.service.ts +++ b/apps/red-ui/src/app/services/reanalysis.service.ts @@ -14,22 +14,22 @@ export class ReanalysisService extends GenericService { @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()