extract file preview user management
This commit is contained in:
parent
d0f96990c7
commit
aecdbcbe7b
@ -0,0 +1,46 @@
|
||||
<iqser-status-bar [configs]="statusBarConfigs" [small]="true"></iqser-status-bar>
|
||||
|
||||
<div class="all-caps-label mr-16 ml-8">
|
||||
{{ translations[file.workflowStatus] | translate }}
|
||||
<span *ngIf="file.isUnderReview || file.isUnderApproval">{{ 'by' | translate }}:</span>
|
||||
</div>
|
||||
|
||||
<redaction-initials-avatar
|
||||
*ngIf="!editingReviewer"
|
||||
[user]="file.assignee"
|
||||
[withName]="!!file.assignee"
|
||||
tooltipPosition="below"
|
||||
></redaction-initials-avatar>
|
||||
|
||||
<div
|
||||
(click)="editingReviewer = true"
|
||||
*ngIf="!editingReviewer && canAssignReviewer"
|
||||
class="assign-reviewer pointer"
|
||||
translate="file-preview.assign-reviewer"
|
||||
></div>
|
||||
|
||||
<redaction-assign-user-dropdown
|
||||
(cancel)="editingReviewer = false"
|
||||
(save)="assignReviewer(file, $event)"
|
||||
*ngIf="editingReviewer"
|
||||
[options]="usersOptions"
|
||||
[value]="file.assignee"
|
||||
></redaction-assign-user-dropdown>
|
||||
|
||||
<div *ngIf="!editingReviewer && canAssign" class="assign-actions-wrapper">
|
||||
<iqser-circle-button
|
||||
(action)="editingReviewer = true"
|
||||
*ngIf="canAssignOrUnassign && !!file.assignee"
|
||||
[tooltip]="assignTooltip"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="fileAssignService.assignToMe([file])"
|
||||
*ngIf="canAssignToSelf"
|
||||
[tooltip]="'file-preview.assign-me' | translate"
|
||||
icon="red:assign-me"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -7,57 +7,14 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-1 actions-container">
|
||||
<iqser-status-bar [configs]="statusBarConfig(file)" [small]="true"></iqser-status-bar>
|
||||
|
||||
<div class="all-caps-label mr-16 ml-8">
|
||||
{{ translations[file.workflowStatus] | translate }}
|
||||
<span *ngIf="isUnderReviewOrApproval(file)">{{ 'by' | translate }}:</span>
|
||||
</div>
|
||||
|
||||
<redaction-initials-avatar
|
||||
*ngIf="!editingReviewer"
|
||||
[user]="file.assignee"
|
||||
[withName]="!!file.assignee"
|
||||
tooltipPosition="below"
|
||||
></redaction-initials-avatar>
|
||||
<div
|
||||
(click)="editingReviewer = true"
|
||||
*ngIf="!editingReviewer && canAssignReviewer(file, dossier)"
|
||||
class="assign-reviewer pointer"
|
||||
translate="file-preview.assign-reviewer"
|
||||
></div>
|
||||
|
||||
<redaction-assign-user-dropdown
|
||||
(cancel)="editingReviewer = false"
|
||||
(save)="assignReviewer(file, $event)"
|
||||
*ngIf="editingReviewer"
|
||||
[options]="usersOptions(file, dossier)"
|
||||
[value]="file.assignee"
|
||||
></redaction-assign-user-dropdown>
|
||||
|
||||
<div *ngIf="canAssign(file)" class="assign-actions-wrapper">
|
||||
<iqser-circle-button
|
||||
(action)="editingReviewer = true"
|
||||
*ngIf="(permissionsService.canAssignUser(file) || permissionsService.canUnassignUser(file)) && !!file.assignee"
|
||||
[tooltip]="assignTooltip(file)"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="fileAssignService.assignToMe([file])"
|
||||
*ngIf="permissionsService.canAssignToSelf(file)"
|
||||
[tooltip]="'file-preview.assign-me' | translate"
|
||||
icon="red:assign-me"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
<redaction-user-management [dossier]="dossier" [file]="file"></redaction-user-management>
|
||||
|
||||
<ng-container *ngIf="permissionsService.isApprover(dossier) && !!file.lastReviewer">
|
||||
<div class="vertical-line"></div>
|
||||
<div class="all-caps-label mr-16 ml-8" translate="file-preview.last-reviewer"></div>
|
||||
<redaction-initials-avatar [user]="file.lastReviewer" [withName]="true"></redaction-initials-avatar>
|
||||
</ng-container>
|
||||
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<redaction-file-actions
|
||||
|
||||
@ -7,11 +7,6 @@
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.assign-reviewer {
|
||||
margin-left: 6px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@ -54,15 +49,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.assign-actions-wrapper {
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep redaction-dictionary-annotation-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@ -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<unknown>;
|
||||
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<void> {
|
||||
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();
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -31,3 +31,10 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
ProcessingFileStatuses.INDEXING,
|
||||
ProcessingFileStatuses.PROCESSING,
|
||||
] as const;
|
||||
|
||||
export interface StatusBarConfig {
|
||||
readonly length: number;
|
||||
readonly color: WorkflowFileStatus;
|
||||
}
|
||||
|
||||
export type StatusBarConfigs = List<StatusBarConfig>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user