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;