From aecdbcbe7bcdcfeee2e046a49f03336628801cb8 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Thu, 25 Nov 2021 12:30:02 +0200 Subject: [PATCH] extract file preview user management --- .../user-management.component.html | 46 ++++++++ .../user-management.component.scss | 17 +++ .../user-management.component.ts | 101 ++++++++++++++++++ .../file-preview-screen.component.html | 47 +------- .../file-preview-screen.component.scss | 14 --- .../file-preview-screen.component.ts | 68 +----------- .../file-preview.module.ts | 2 + .../file-actions/file-actions.component.ts | 1 - .../src/app/services/permissions.service.ts | 12 +-- libs/red-domain/src/lib/files/types.ts | 7 ++ 10 files changed, 180 insertions(+), 135 deletions(-) create mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.html create mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.scss create mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.html new file mode 100644 index 000000000..52b95aaf7 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.html @@ -0,0 +1,46 @@ + + +
+ {{ translations[file.workflowStatus] | translate }} + {{ 'by' | translate }}: +
+ + + +
+ + + +
+ + + +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.scss b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.scss new file mode 100644 index 000000000..cfa1c0158 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.scss @@ -0,0 +1,17 @@ +:host { + display: contents; +} + +.assign-reviewer { + margin-left: 6px; + text-decoration: underline; +} + +.assign-actions-wrapper { + display: flex; + margin-left: 8px; + + > *:not(:last-child) { + margin-right: 2px; + } +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts new file mode 100644 index 000000000..564aa9300 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts @@ -0,0 +1,101 @@ +import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; +import { Dossier, File, StatusBarConfigs, User } from '@red/domain'; +import { List, LoadingService, Toaster } from '@iqser/common-ui'; +import { PermissionsService } from '@services/permissions.service'; +import { FileAssignService } from '../../../../shared/services/file-assign.service'; +import { workflowFileStatusTranslations } from '../../../../translations/file-status-translations'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { UserService } from '@services/user.service'; +import { FilesService } from '@services/entity-services/files.service'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'redaction-user-management', + templateUrl: './user-management.component.html', + styleUrls: ['./user-management.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UserManagementComponent implements OnChanges { + readonly translations = workflowFileStatusTranslations; + + @Input() file: File; + @Input() dossier: Dossier; + + editingReviewer = false; + statusBarConfigs: StatusBarConfigs; + canAssignToSelf = false; + canAssignUser = false; + canUnassignUser = false; + canAssign = false; + canAssignOrUnassign = false; + canAssignReviewer = false; + assignTooltip: string; + usersOptions: List; + + constructor( + readonly fileAssignService: FileAssignService, + readonly permissionsService: PermissionsService, + readonly userService: UserService, + readonly filesService: FilesService, + readonly toaster: Toaster, + readonly loadingService: LoadingService, + readonly translateService: TranslateService, + ) {} + + private get _statusBarConfig(): StatusBarConfigs { + return [{ length: 1, color: this.file.workflowStatus }]; + } + + private get _assignOrChangeReviewerTooltip(): string { + return this.file.assignee + ? this.translateService.instant('file-preview.change-reviewer') + : this.translateService.instant('file-preview.assign-reviewer'); + } + + private get _assignTooltip(): string { + return this.file.isUnderApproval + ? this.translateService.instant('dossier-overview.assign-approver') + : this._assignOrChangeReviewerTooltip; + } + + private get _canAssignReviewer(): boolean { + return !this.file.assignee && this.canAssignUser && this.dossier.hasReviewers; + } + + private get _usersOptions(): List { + const unassignUser = this.canUnassignUser ? [undefined] : []; + return this.file.isUnderApproval ? [...this.dossier.approverIds, ...unassignUser] : [...this.dossier.memberIds, ...unassignUser]; + } + + ngOnChanges() { + this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file, this.dossier); + this.canAssignUser = this.permissionsService.canAssignUser(this.file, this.dossier); + this.canUnassignUser = this.permissionsService.canUnassignUser(this.file, this.dossier); + this.canAssignOrUnassign = this.canAssignUser || this.canUnassignUser; + this.canAssign = this.canAssignToSelf || this.canAssignOrUnassign; + + this.statusBarConfigs = this._statusBarConfig; + this.canAssignReviewer = this._canAssignReviewer; + this.assignTooltip = this._assignTooltip; + this.usersOptions = this._usersOptions; + } + + async assignReviewer(file: File, user: User | string) { + const assigneeId = typeof user === 'string' ? user : user?.id; + const reviewerName = this.userService.getNameForId(assigneeId); + + const { dossierId, fileId, filename } = file; + this.loadingService.start(); + if (!assigneeId) { + await this.filesService.setUnassigned([fileId], dossierId).toPromise(); + } else if (file.isUnderReview) { + await this.filesService.setReviewerFor([fileId], dossierId, assigneeId).toPromise(); + } else if (file.isUnderApproval) { + await this.filesService.setUnderApprovalFor([fileId], dossierId, assigneeId).toPromise(); + } + this.loadingService.stop(); + + this.toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } }); + this.editingReviewer = false; + } +} 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 80c24d4af..a9dcd1937 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 @@ -7,57 +7,14 @@
- - -
- {{ translations[file.workflowStatus] | translate }} - {{ 'by' | translate }}: -
- - -
- - - -
- - - -
+
+
*:not(:last-child) { - margin-right: 2px; - } -} - ::ng-deep redaction-dictionary-annotation-icon { margin-right: 8px; } 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 6cb5ec2a2..a88489f0b 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 @@ -18,23 +18,20 @@ import { CircleButtonTypes, Debounce, FilterService, - List, LoadingService, OnAttach, OnDetach, processFilters, shareDistinctLast, - Toaster, } from '@iqser/common-ui'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { ManualAnnotationResponse } from '@models/file/manual-annotation-response'; import { AnnotationData, FileDataModel } from '@models/file/file-data.model'; -import { FileAssignService } from '../../shared/services/file-assign.service'; import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; -import { Dossier, File, User, ViewMode, WorkflowFileStatus } from '@red/domain'; +import { Dossier, File, ViewMode } from '@red/domain'; import { PermissionsService } from '@services/permissions.service'; import { combineLatest, Observable, timer } from 'rxjs'; import { UserPreferenceService } from '@services/user-preference.service'; @@ -45,9 +42,7 @@ import { FileWorkloadComponent } from './components/file-workload/file-workload. import { DossiersDialogService } from '../../services/dossiers-dialog.service'; import { clearStamps, stampPDFPage } from '@utils/page-stamper'; import { TranslateService } from '@ngx-translate/core'; -import { workflowFileStatusTranslations } from '../../translations/file-status-translations'; import { handleFilterDelta } from '@utils/filter-utils'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { FileActionsComponent } from '../../shared/components/file-actions/file-actions.component'; import { FilesService } from '@services/entity-services/files.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; @@ -70,11 +65,9 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; }) export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach { readonly circleButtonTypes = CircleButtonTypes; - readonly translations = workflowFileStatusTranslations; dialogRef: MatDialogRef; fullScreen = false; - editingReviewer = false; shouldDeselectAnnotationsOnPageChange = true; fileData: FileDataModel; annotationData: AnnotationData; @@ -110,10 +103,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _activatedRoute: ActivatedRoute, private readonly _dialogService: DossiersDialogService, private readonly _router: Router, - private readonly _toaster: Toaster, private readonly _annotationProcessingService: AnnotationProcessingService, private readonly _annotationDrawService: AnnotationDrawService, - readonly fileAssignService: FileAssignService, private readonly _fileDownloadService: PdfViewerDataService, private readonly _filesService: FilesService, private readonly _ngZone: NgZone, @@ -180,44 +171,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni ); } - assignTooltip(file: File): string { - return file.isUnderApproval - ? this._translateService.instant('dossier-overview.assign-approver') - : this.assignOrChangeReviewerTooltip(file); - } - - assignOrChangeReviewerTooltip(file: File): string { - return file.assignee - ? this._translateService.instant('file-preview.change-reviewer') - : this._translateService.instant('file-preview.assign-reviewer'); - } - - statusBarConfig(file: File): [{ length: number; color: WorkflowFileStatus }] { - return [{ length: 1, color: file.workflowStatus }]; - } - - isUnderReviewOrApproval(file: File): boolean { - return file.isUnderReview || file.isUnderApproval; - } - - canAssign(file: File): boolean { - return ( - !this.editingReviewer && - (this.permissionsService.canAssignUser(file) || - this.permissionsService.canAssignToSelf(file) || - this.permissionsService.canUnassignUser(file)) - ); - } - - usersOptions(file: File, dossier: Dossier): List { - const unassignUser = this.permissionsService.canUnassignUser(file) ? [undefined] : []; - return file.isUnderApproval ? [...dossier.approverIds, ...unassignUser] : [...dossier.memberIds, ...unassignUser]; - } - - canAssignReviewer(file: File, dossier: Dossier): boolean { - return !file.assignee && this.permissionsService.canAssignUser(file) && dossier.hasReviewers; - } - async updateViewMode(): Promise { const annotations = this._getAnnotations(a => a.getCustomData('redacto-manager')); const redactions = annotations.filter(a => a.getCustomData('redaction')); @@ -472,25 +425,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni this.excludedPagesService.hide(); } - async assignReviewer(file: File, user: User | string) { - const assigneeId = typeof user === 'string' ? user : user?.id; - const reviewerName = this._userService.getNameForId(assigneeId); - - const { dossierId, fileId, filename } = file; - this._loadingService.start(); - if (!assigneeId) { - await this._filesService.setUnassigned([fileId], dossierId).toPromise(); - } else if (file.isUnderReview) { - await this._filesService.setReviewerFor([fileId], dossierId, assigneeId).toPromise(); - } else if (file.isUnderApproval) { - await this._filesService.setUnderApprovalFor([fileId], dossierId, assigneeId).toPromise(); - } - this._loadingService.stop(); - - this._toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } }); - this.editingReviewer = false; - } - closeFullScreen() { if (!!document.fullscreenElement && document.exitFullscreen) { document.exitFullscreen().then(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts index e13fe0088..f20db45ac 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts @@ -19,6 +19,7 @@ import { TypeAnnotationIconComponent } from './components/type-annotation-icon/t import { OverlayModule } from '@angular/cdk/overlay'; import { ViewSwitchComponent } from './components/view-switch/view-switch.component'; import { ViewModeService } from './services/view-mode.service'; +import { UserManagementComponent } from './components/user-management/user-management.component'; const routes: Routes = [ { @@ -43,6 +44,7 @@ const routes: Routes = [ DocumentInfoComponent, TypeAnnotationIconComponent, ViewSwitchComponent, + UserManagementComponent, ], imports: [ RouterModule.forChild(routes), diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index 2979bd75f..e60d75c70 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -218,7 +218,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, this.showStatusBar = !this.file.isError && !this.file.isPending && this.isDossierOverviewList; this.showAssignToSelf = this._permissionsService.canAssignToSelf(this.file) && this.isDossierOverview; - console.log(this.showAssignToSelf); this.showAssign = (this._permissionsService.canAssignUser(this.file) || this._permissionsService.canUnassignUser(this.file)) && this.isDossierOverview; diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index 4165546cc..65f8c7b24 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -35,14 +35,12 @@ export class PermissionsService { return (this.isOwner(dossier) && !file.isApproved) || file.isNew; } - canAssignToSelf(file: File): boolean { - const dossier = this._getDossier(file); + canAssignToSelf(file: File, dossier = this._getDossier(file)): boolean { const precondition = this.isDossierMember(dossier) && !this.isFileAssignee(file) && !file.isError && !file.isProcessing; return precondition && (file.isNew || file.isUnderReview || (file.isUnderApproval && this.isApprover(dossier))); } - canAssignUser(file: File): boolean { - const dossier = this._getDossier(file); + canAssignUser(file: File, dossier = this._getDossier(file)): boolean { const precondition = !file.isProcessing && !file.isError && !file.isApproved && this.isApprover(dossier); if (precondition) { @@ -56,13 +54,11 @@ export class PermissionsService { return false; } - canUnassignUser(file: File): boolean { - const dossier = this._getDossier(file); + canUnassignUser(file: File, dossier = this._getDossier(file)): boolean { return (file.isUnderReview || file.isUnderApproval) && (this.isFileAssignee(file) || this.isApprover(dossier)); } - canSetUnderReview(file: File): boolean { - const dossier = this._getDossier(file); + canSetUnderReview(file: File, dossier = this._getDossier(file)): boolean { return file.isUnderApproval && this.isApprover(dossier); } diff --git a/libs/red-domain/src/lib/files/types.ts b/libs/red-domain/src/lib/files/types.ts index 5ffb3fc75..dce23e0a1 100644 --- a/libs/red-domain/src/lib/files/types.ts +++ b/libs/red-domain/src/lib/files/types.ts @@ -31,3 +31,10 @@ export const isProcessingStatuses: List = [ ProcessingFileStatuses.INDEXING, ProcessingFileStatuses.PROCESSING, ] as const; + +export interface StatusBarConfig { + readonly length: number; + readonly color: WorkflowFileStatus; +} + +export type StatusBarConfigs = List;