work in progress permission updates
This commit is contained in:
parent
1c5f8fd006
commit
1199fe4542
@ -1,80 +1,96 @@
|
||||
<button
|
||||
(click)="openDeleteFileDialog($event, fileStatus)"
|
||||
*ngIf="permissionsService.canDeleteFile(fileStatus)"
|
||||
[matTooltip]="'project-overview.delete.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<div class="file-actions">
|
||||
<button
|
||||
(click)="openDeleteFileDialog($event, fileStatus)"
|
||||
*ngIf="permissionsService.canDeleteFile(fileStatus)"
|
||||
[matTooltip]="'project-overview.delete.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||
</button>
|
||||
<div
|
||||
[matTooltip]="(fileStatus.isApproved ? 'report.action' : 'report.unavailable-single') | translate"
|
||||
*ngIf="permissionsService.canShowRedactionReportDownloadBtn(fileStatus)"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<button (click)="downloadFileRedactionReport($event, fileStatus)" [disabled]="!fileStatus.isApproved" color="accent" mat-icon-button>
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
[matTooltip]="(fileStatus.isApproved ? 'report.action' : 'report.unavailable-single') | translate"
|
||||
*ngIf="permissionsService.canShowRedactionReportDownloadBtn()"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<button (click)="downloadFileRedactionReport($event, fileStatus)" [disabled]="!fileStatus.isApproved" color="accent" mat-icon-button>
|
||||
<mat-icon svgIcon="red:report"></mat-icon>
|
||||
<button
|
||||
(click)="reanalyseFile($event, fileStatus)"
|
||||
class="warn"
|
||||
*ngIf="permissionsService.canReanalyseFile(fileStatus) && screen === 'file-preview'"
|
||||
mat-icon-button
|
||||
#reanalyseTooltip="matTooltip"
|
||||
[matTooltip]="'file-preview.reanalyse-notification' | translate"
|
||||
matTooltipClass="warn"
|
||||
>
|
||||
<mat-icon svgIcon="red:refresh"></mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="reanalyseFile($event, fileStatus)"
|
||||
*ngIf="permissionsService.canReanalyseFile(fileStatus) && screen !== 'file-preview'"
|
||||
[matTooltip]="'project-overview.reanalyse.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:refresh"></mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="assignReviewer($event, fileStatus)"
|
||||
*ngIf="permissionsService.canAssignReviewer(fileStatus)"
|
||||
[matTooltip]="'project-overview.assign.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="setFileApproved($event, fileStatus)"
|
||||
*ngIf="permissionsService.canApprove(fileStatus)"
|
||||
[matTooltip]="'project-overview.approve' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:check-alt"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderApproval($event, fileStatus)"
|
||||
*ngIf="permissionsService.canSetUnderApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-approval' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:check-alt"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderApproval($event, fileStatus)"
|
||||
*ngIf="permissionsService.canUndoApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-approval' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:undo"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderReview($event, fileStatus)"
|
||||
*ngIf="permissionsService.canUndoUnderApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-review' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:undo"></mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
(click)="assignReviewer($event, fileStatus)"
|
||||
*ngIf="permissionsService.canAssignReviewer(fileStatus)"
|
||||
[matTooltip]="'project-overview.assign.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:assign"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="reanalyseFile($event, fileStatus)"
|
||||
*ngIf="permissionsService.canReanalyseFile(fileStatus)"
|
||||
[matTooltip]="'project-overview.reanalyse.action' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:refresh"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileApproved($event, fileStatus)"
|
||||
*ngIf="permissionsService.canApprove(fileStatus)"
|
||||
[matTooltip]="'project-overview.approve' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:check-alt"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderApproval($event, fileStatus)"
|
||||
*ngIf="permissionsService.canSetUnderApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-approval' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:check-alt"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderApproval($event, fileStatus)"
|
||||
*ngIf="permissionsService.canUndoApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-approval' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:undo"></mat-icon>
|
||||
</button>
|
||||
<button
|
||||
(click)="setFileUnderReview($event, fileStatus)"
|
||||
*ngIf="permissionsService.canUndoUnderApproval(fileStatus)"
|
||||
[matTooltip]="'project-overview.under-review' | translate"
|
||||
matTooltipPosition="above"
|
||||
color="accent"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon svgIcon="red:undo"></mat-icon>
|
||||
</button>
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
.file-actions {
|
||||
display: flex;
|
||||
}
|
||||
@ -1,27 +1,51 @@
|
||||
import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
|
||||
import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '../service/permissions.service';
|
||||
import { FileStatusWrapper } from '../../screens/file/model/file-status.wrapper';
|
||||
import { DialogService } from '../../dialogs/dialog.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { FileActionService } from '../../screens/file/service/file-action.service';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-actions',
|
||||
templateUrl: './file-actions.component.html',
|
||||
styleUrls: ['./file-actions.component.scss']
|
||||
})
|
||||
export class FileActionsComponent implements OnInit {
|
||||
export class FileActionsComponent implements OnInit, AfterViewInit {
|
||||
@Input() fileStatus: FileStatusWrapper;
|
||||
@Output() actionPerformed = new EventEmitter<string>();
|
||||
@ViewChild('reanalyseTooltip') private _reanalyseTooltip: MatTooltip;
|
||||
|
||||
screen: 'file-preview' | 'project-overview';
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _dialogService: DialogService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _fileActionService: FileActionService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
ngOnInit(): void {
|
||||
if (!this.fileStatus) {
|
||||
this.fileStatus = this.appStateService.activeFile;
|
||||
this.screen = 'file-preview';
|
||||
this.appStateService.fileChanged.subscribe((fileStatus: FileStatusWrapper) => {
|
||||
if (fileStatus.fileId === this.fileStatus?.fileId) {
|
||||
this.fileStatus = this.appStateService.activeFile;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.screen = 'project-overview';
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
setTimeout(() => {
|
||||
if (this._reanalyseTooltip) {
|
||||
this._reanalyseTooltip.show();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
|
||||
this._dialogService.openDeleteFileDialog($event, fileStatusWrapper.projectId, fileStatusWrapper.fileId, () => {
|
||||
@ -31,7 +55,7 @@ export class FileActionsComponent implements OnInit {
|
||||
|
||||
downloadFileRedactionReport($event: MouseEvent, file: FileStatusWrapper) {
|
||||
$event.stopPropagation();
|
||||
this._appStateService.downloadFileRedactionReport(file);
|
||||
this.appStateService.downloadFileRedactionReport(file);
|
||||
}
|
||||
|
||||
assignReviewer($event: MouseEvent, file: FileStatusWrapper) {
|
||||
@ -67,8 +91,8 @@ export class FileActionsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public reloadProjects(action: string) {
|
||||
this._appStateService.getFiles().then(() => {
|
||||
reloadProjects(action: string) {
|
||||
this.appStateService.getFiles().then(() => {
|
||||
this.actionPerformed.emit(action);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FilterModel } from '../model/filter.model';
|
||||
import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper';
|
||||
import { ProjectWrapper } from '../../../state/app-state.service';
|
||||
import { ProjectWrapper } from '../../../state/model/project.wrapper';
|
||||
|
||||
export const RedactionFilterSorter = {
|
||||
hint: 1,
|
||||
|
||||
@ -19,22 +19,34 @@ export class PermissionsService {
|
||||
}
|
||||
|
||||
isReviewerOrOwner(fileStatus?: FileStatusWrapper, user?: User) {
|
||||
return this.isActiveFileDocumentReviewer() || this.isManagerAndOwner();
|
||||
return this.isFileReviewer(fileStatus) || this.isManagerAndOwner();
|
||||
}
|
||||
|
||||
fileRequiresReanalysis(fileStatus?: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return (
|
||||
((fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') &&
|
||||
(fileStatus.dictionaryVersion !== this._appStateService.dictionaryVersion ||
|
||||
fileStatus.rulesVersion !== this._appStateService.rulesVersion ||
|
||||
fileStatus.hasUnappliedSuggestions)) ||
|
||||
fileStatus.isError
|
||||
);
|
||||
}
|
||||
|
||||
canReanalyseFile(fileStatus?: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
// can reanalyse file if error, has requests not up to date with dictionary and is owner or reviewer
|
||||
return (
|
||||
((!fileStatus.isApproved && this._appStateService.fileNotUpToDateWithDictionary(fileStatus)) || fileStatus.isError || fileStatus.hasRequests) &&
|
||||
(this.isManagerAndOwner() || this.isActiveFileDocumentReviewer())
|
||||
);
|
||||
return this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus);
|
||||
}
|
||||
|
||||
isActiveFileDocumentReviewer() {
|
||||
return this._appStateService.activeFile?.currentReviewer === this._userService.userId;
|
||||
isFileReviewer(fileStatus?: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return fileStatus.currentReviewer === this._userService.userId;
|
||||
}
|
||||
|
||||
canDeleteFile(fileStatus?: FileStatusWrapper) {
|
||||
@ -74,7 +86,7 @@ export class PermissionsService {
|
||||
user = this._userService.user;
|
||||
}
|
||||
if (!project) {
|
||||
project = this._appStateService.activeProject;
|
||||
project = this._appStateService.activeProject.project;
|
||||
}
|
||||
return user.isManager && project.ownerId === user.id;
|
||||
}
|
||||
@ -84,7 +96,7 @@ export class PermissionsService {
|
||||
user = this._userService.user;
|
||||
}
|
||||
if (!project) {
|
||||
project = this._appStateService.activeProject;
|
||||
project = this._appStateService.activeProject.project;
|
||||
}
|
||||
return project.memberIds?.includes(user.id);
|
||||
}
|
||||
@ -93,14 +105,14 @@ export class PermissionsService {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return (fileStatus.status === 'UNDER_APPROVAL' || fileStatus.status === 'UNDER_REVIEW') && this._userService.userId === fileStatus.currentReviewer;
|
||||
return (fileStatus.status === 'UNDER_APPROVAL' || fileStatus.status === 'UNDER_REVIEW') && this.isFileReviewer(fileStatus);
|
||||
}
|
||||
|
||||
public canOpenFile(fileStatus: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return !fileStatus.isError && !fileStatus.isProcessing;
|
||||
return !fileStatus.isError && !fileStatus.isProcessing && this.isReviewerOrOwner(fileStatus);
|
||||
}
|
||||
|
||||
canShowRedactionReportDownloadBtn(fileStatus?: FileStatusWrapper) {
|
||||
@ -114,7 +126,21 @@ export class PermissionsService {
|
||||
return this.isProjectMember() && !fileStatus.isError && !fileStatus.isApprovedOrUnderApproval;
|
||||
}
|
||||
|
||||
canUndoApproval(fileStatus: any) {}
|
||||
canUndoApproval(fileStatus: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return fileStatus.status === 'APPROVED' && this.isManagerAndOwner();
|
||||
}
|
||||
|
||||
canUndoUnderApproval(fileStatus: any) {}
|
||||
canUndoUnderApproval(fileStatus: any) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this._appStateService.activeFile;
|
||||
}
|
||||
return fileStatus.status === 'UNDER_APPROVAL' && this.isManagerAndOwner();
|
||||
}
|
||||
|
||||
canMarkPagesAsViewed() {
|
||||
return this.isReviewerOrOwner();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { UserService } from '../../user/user.service';
|
||||
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
|
||||
import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service';
|
||||
import { ManualAnnotationResponse } from '../../screens/file/model/manual-annotation-response';
|
||||
import { PermissionsService } from '../../common/service/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-manual-annotation-dialog',
|
||||
@ -30,12 +31,13 @@ export class ManualAnnotationDialogComponent implements OnInit {
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
public dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.isDocumentAdmin = this._appStateService.isActiveProjectOwnerAndManager;
|
||||
this.isDocumentAdmin = this._permissionsService.isManagerAndOwner();
|
||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||
|
||||
this.isDictionaryRequest = this.manualRedactionEntryWrapper.type === 'DICTIONARY';
|
||||
@ -60,18 +62,14 @@ export class ManualAnnotationDialogComponent implements OnInit {
|
||||
|
||||
handleAddRedaction() {
|
||||
this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry);
|
||||
this._manualAnnotationService
|
||||
.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry)
|
||||
.subscribe(
|
||||
(response) => {
|
||||
this.dialogRef.close(
|
||||
new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response)
|
||||
);
|
||||
},
|
||||
() => {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
);
|
||||
this._manualAnnotationService.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry).subscribe(
|
||||
(response) => {
|
||||
this.dialogRef.close(new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response));
|
||||
},
|
||||
() => {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
get title() {
|
||||
@ -80,8 +78,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
|
||||
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
|
||||
addRedactionRequest.addToDictionary =
|
||||
this.manualRedactionEntryWrapper.type === 'DICTIONARY';
|
||||
addRedactionRequest.addToDictionary = this.manualRedactionEntryWrapper.type === 'DICTIONARY';
|
||||
addRedactionRequest.reason = this.redactionForm.get('reason').value;
|
||||
// todo fix this in backend
|
||||
if (!addRedactionRequest.reason) {
|
||||
|
||||
@ -12,14 +12,14 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-1 filename page-title">
|
||||
<span *ngIf="appStateService.fileNotUpToDateWithDictionary()" class="pill" translate="project-overview.new-rule.label"></span>
|
||||
<span *ngIf="permissionsService.fileRequiresReanalysis()" class="pill" translate="project-overview.new-rule.label"></span>
|
||||
<span *ngIf="!permissionsService.canPerformAnnotationActions()" class="pill" translate="readonly-pill"></span> <span>{{
|
||||
appStateService.activeFile.filename
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 actions-container">
|
||||
<redaction-file-actions [fileStatus]="fileData.fileStatus" (actionPerformed)="fileActionPerformed()"></redaction-file-actions>
|
||||
<redaction-file-actions (actionPerformed)="fileActionPerformed($event)" *ngIf="viewReady"></redaction-file-actions>
|
||||
<button [routerLink]="['/ui/projects/' + appStateService.activeProjectId]" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
|
||||
@ -20,6 +20,7 @@ redaction-pdf-viewer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.right-fixed-container {
|
||||
|
||||
@ -40,7 +40,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||
@ViewChild('reanalyseTooltip') private _reanalyseTooltip: MatTooltip;
|
||||
|
||||
fileData: FileDataModel;
|
||||
fileId: string;
|
||||
@ -98,17 +97,19 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
get canNotSwitchToRedactedView() {
|
||||
return this.appStateService.fileNotUpToDateWithDictionary() || this.fileData?.entriesToAdd?.length > 0;
|
||||
return this.permissionsService.fileRequiresReanalysis();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
|
||||
this._loadFileData().subscribe(() => {});
|
||||
this._loadFileData().subscribe(() => {
|
||||
this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
|
||||
});
|
||||
this.appStateService.fileReanalysed.subscribe((fileStatus: FileStatusWrapper) => {
|
||||
if (fileStatus.fileId === this.fileId) {
|
||||
this._loadFileData().subscribe(() => {
|
||||
this.viewReady = true;
|
||||
this.loadingMessage = null;
|
||||
this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
|
||||
this._cleanupAndRedrawManualAnnotations();
|
||||
});
|
||||
}
|
||||
@ -353,9 +354,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
||||
viewerReady($event: WebViewerInstance) {
|
||||
this.instance = $event;
|
||||
this.viewReady = true;
|
||||
if (this._reanalyseTooltip) {
|
||||
this._reanalyseTooltip.show();
|
||||
}
|
||||
this._cleanupAndRedrawManualAnnotations();
|
||||
}
|
||||
|
||||
|
||||
@ -11,9 +11,8 @@ export class FileStatusWrapper {
|
||||
return this.fileStatus.added;
|
||||
}
|
||||
|
||||
// TODO use this for suggestions
|
||||
get allManualRedactionsApplied() {
|
||||
return this.fileStatus.allManualRedactionsApplied;
|
||||
get hasUnappliedSuggestions() {
|
||||
return !this.fileStatus.allManualRedactionsApplied;
|
||||
}
|
||||
|
||||
get currentReviewer() {
|
||||
|
||||
@ -1,15 +1,7 @@
|
||||
import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
SimpleChanges
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, HostListener, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||
import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-page-indicator',
|
||||
@ -28,11 +20,12 @@ export class PageIndicatorComponent implements OnChanges, OnInit {
|
||||
|
||||
constructor(
|
||||
private readonly _viewedPagesControllerService: ViewedPagesControllerService,
|
||||
private readonly _appStateService: AppStateService
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _permissionService: PermissionsService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.canMarkPagesAsViewed = this._appStateService.canMarkPagesAsViewedForActiveFile;
|
||||
this.canMarkPagesAsViewed = this._permissionService.canMarkPagesAsViewed();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
@ -62,26 +55,16 @@ export class PageIndicatorComponent implements OnChanges, OnInit {
|
||||
|
||||
private _markPageRead() {
|
||||
this._viewedPagesControllerService
|
||||
.addPage(
|
||||
{ page: this.number },
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId
|
||||
)
|
||||
.addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId)
|
||||
.subscribe(() => {
|
||||
this.viewedPages?.pages?.push(this.number);
|
||||
});
|
||||
}
|
||||
|
||||
private _markPageUnread() {
|
||||
this._viewedPagesControllerService
|
||||
.removePage(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId,
|
||||
this.number
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
|
||||
});
|
||||
this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => {
|
||||
this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
|
||||
});
|
||||
}
|
||||
|
||||
// @HostListener('window:keydown', ['$event'])
|
||||
|
||||
@ -1,16 +1,4 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Input,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
||||
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
@ -171,36 +159,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
type: 'actionButton',
|
||||
dataElement: 'add-dictionary',
|
||||
img: '/assets/icons/general/add-dictionary.svg',
|
||||
title: this._translateService.instant(
|
||||
this._manualAnnotationService.getTitle('DICTIONARY')
|
||||
),
|
||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')),
|
||||
onClick: () => {
|
||||
const mre = this._getManualRedactionEntry();
|
||||
this.manualAnnotationRequested.emit(
|
||||
new ManualRedactionEntryWrapper(
|
||||
this.instance.docViewer.getSelectedTextQuads(),
|
||||
mre,
|
||||
'DICTIONARY'
|
||||
)
|
||||
);
|
||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'DICTIONARY'));
|
||||
}
|
||||
});
|
||||
this.instance.textPopup.add(<any>{
|
||||
type: 'actionButton',
|
||||
dataElement: 'add-redaction',
|
||||
img: '/assets/icons/general/add-redaction.svg',
|
||||
title: this._translateService.instant(
|
||||
this._manualAnnotationService.getTitle('REDACTION')
|
||||
),
|
||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
|
||||
onClick: () => {
|
||||
const mre = this._getManualRedactionEntry();
|
||||
this.manualAnnotationRequested.emit(
|
||||
new ManualRedactionEntryWrapper(
|
||||
this.instance.docViewer.getSelectedTextQuads(),
|
||||
mre,
|
||||
'REDACTION'
|
||||
)
|
||||
);
|
||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'REDACTION'));
|
||||
}
|
||||
});
|
||||
this._handleCustomActions();
|
||||
@ -292,14 +264,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
}
|
||||
|
||||
private _restoreState(viewerState: ViewerState, instance: WebViewerInstance) {
|
||||
if (this._viewerState) {
|
||||
if (viewerState) {
|
||||
instance.docViewer.setCurrentPage(viewerState.pageNumber);
|
||||
instance.setLayoutMode(viewerState.layoutMode);
|
||||
const instanceDisplayMode = instance.docViewer.getDisplayModeManager().getDisplayMode();
|
||||
instanceDisplayMode.mode = viewerState.displayMode;
|
||||
instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
|
||||
// Synchronize zoom - needs to be done before scrolling
|
||||
instance.docViewer.zoomTo(viewerState.zoom);
|
||||
if (viewerState.zoom === 0) {
|
||||
this.instance.setFitMode('FitPage');
|
||||
} else {
|
||||
instance.docViewer.zoomTo(viewerState.zoom);
|
||||
}
|
||||
const viewerScrollElement = instance.docViewer.getScrollViewElement();
|
||||
viewerScrollElement.scrollTo(viewerState.scrollLeft, viewerState.scrollTop);
|
||||
|
||||
|
||||
@ -4,11 +4,13 @@ import { map, tap } from 'rxjs/operators';
|
||||
import {
|
||||
FileUploadControllerService,
|
||||
ManualRedactionControllerService,
|
||||
RedactionLogControllerService
|
||||
RedactionLogControllerService,
|
||||
ViewedPagesControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { FileType } from '../model/file-type';
|
||||
import { FileDataModel } from '../model/file-data.model';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -16,80 +18,69 @@ import { AppStateService } from '../../../state/app-state.service';
|
||||
export class FileDownloadService {
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _redactionLogControllerService: RedactionLogControllerService
|
||||
private readonly _redactionLogControllerService: RedactionLogControllerService,
|
||||
private readonly _viewedPagesControllerService: ViewedPagesControllerService
|
||||
) {}
|
||||
|
||||
public loadActiveFileManualAnnotations() {
|
||||
return this._manualRedactionControllerService.getManualRedaction(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId
|
||||
);
|
||||
return this._manualRedactionControllerService.getManualRedaction(this._appStateService.activeProjectId, this._appStateService.activeFileId);
|
||||
}
|
||||
|
||||
public loadActiveFileData(): Observable<FileDataModel> {
|
||||
const annotatedObs = this.loadFile('ANNOTATED', this._appStateService.activeFileId);
|
||||
const redactedObs = this.loadFile('REDACTED', this._appStateService.activeFileId);
|
||||
const reactionLogObs = this._redactionLogControllerService.getRedactionLog(
|
||||
this._appStateService.activeFileId
|
||||
);
|
||||
const reactionLogObs = this._redactionLogControllerService.getRedactionLog(this._appStateService.activeFileId);
|
||||
const manualRedactionsObs = this._manualRedactionControllerService.getManualRedaction(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId
|
||||
);
|
||||
const viewedPagesObs = this._appStateService.getViewedPagesForActiveFile();
|
||||
const viewedPagesObs = this.getViewedPagesForActiveFile();
|
||||
|
||||
return forkJoin([
|
||||
annotatedObs,
|
||||
redactedObs,
|
||||
reactionLogObs,
|
||||
manualRedactionsObs,
|
||||
viewedPagesObs
|
||||
]).pipe(map((data) => new FileDataModel(this._appStateService.activeFile, ...data)));
|
||||
return forkJoin([annotatedObs, redactedObs, reactionLogObs, manualRedactionsObs, viewedPagesObs]).pipe(
|
||||
map((data) => new FileDataModel(this._appStateService.activeFile, ...data))
|
||||
);
|
||||
}
|
||||
|
||||
loadFile(
|
||||
fileType: FileType | string,
|
||||
fileId: string,
|
||||
saveTo: (fileData) => void = () => null,
|
||||
fetch: () => any = () => null
|
||||
): Observable<any> {
|
||||
getViewedPagesForActiveFile() {
|
||||
if (this._permissionsService.canMarkPagesAsViewed()) {
|
||||
return this._viewedPagesControllerService.getViewedPages(this._appStateService.activeProjectId, this._appStateService.activeFileId);
|
||||
}
|
||||
return of({ pages: [] });
|
||||
}
|
||||
|
||||
loadFile(fileType: FileType | string, fileId: string, saveTo: (fileData) => void = () => null, fetch: () => any = () => null): Observable<any> {
|
||||
let fileObs$: Observable<any>;
|
||||
switch (fileType) {
|
||||
case FileType.ANNOTATED:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadAnnotatedFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
: this._fileUploadControllerService.downloadAnnotatedFile(fileId, true, 'body').pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
case FileType.REDACTED:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadRedactedFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
: this._fileUploadControllerService.downloadRedactedFile(fileId, true, 'body').pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
case FileType.ORIGINAL:
|
||||
default:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadOriginalFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
: this._fileUploadControllerService.downloadOriginalFile(fileId, true, 'body').pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
return fileObs$;
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import {
|
||||
DictionaryControllerService,
|
||||
ManualRedactionControllerService,
|
||||
ManualRedactionEntry
|
||||
} from '@redaction/red-ui-http';
|
||||
import { DictionaryControllerService, ManualRedactionControllerService, ManualRedactionEntry } from '@redaction/red-ui-http';
|
||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||
import { NotificationService, NotificationType } from '../../../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { UserService } from '../../../user/user.service';
|
||||
import { PermissionsService } from '../../../common/service/permissions.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -21,7 +18,8 @@ export class ManualAnnotationService {
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
// Comments
|
||||
@ -49,7 +47,7 @@ export class ManualAnnotationService {
|
||||
// /manualRedaction/redaction/add
|
||||
// /manualRedaction/request/add
|
||||
addAnnotation(manualRedactionEntry: ManualRedactionEntry) {
|
||||
if (this._appStateService.isActiveProjectOwnerAndManager) {
|
||||
if (this._permissionsService.isManagerAndOwner()) {
|
||||
return this._makeRedaction(manualRedactionEntry);
|
||||
} else {
|
||||
return this._makeRedactionRequest(manualRedactionEntry);
|
||||
@ -81,19 +79,12 @@ export class ManualAnnotationService {
|
||||
|
||||
undoRequest(annotationWrapper: AnnotationWrapper) {
|
||||
return this._manualRedactionControllerService
|
||||
.undo(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId,
|
||||
annotationWrapper.id
|
||||
)
|
||||
.undo(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.undo-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.undo-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -103,39 +94,25 @@ export class ManualAnnotationService {
|
||||
// /manualRedaction/decline/remove
|
||||
// /manualRedaction/undo
|
||||
declineOrRemoveRequest(annotationWrapper: AnnotationWrapper) {
|
||||
if (this._appStateService.isActiveProjectOwnerAndManager) {
|
||||
if (this._permissionsService.isManagerAndOwner()) {
|
||||
return this._manualRedactionControllerService
|
||||
.declineRequest(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId,
|
||||
annotationWrapper.id
|
||||
)
|
||||
.declineRequest(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.undo-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.undo-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return this._manualRedactionControllerService
|
||||
.undo(
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId,
|
||||
annotationWrapper.id
|
||||
)
|
||||
.undo(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.undo-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.undo-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -145,11 +122,8 @@ export class ManualAnnotationService {
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/remove/
|
||||
// /manualRedaction/request/remove/
|
||||
removeOrSuggestRemoveAnnotation(
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
removeFromDictionary: boolean = false
|
||||
) {
|
||||
if (this._appStateService.isActiveProjectOwnerAndManager) {
|
||||
removeOrSuggestRemoveAnnotation(annotationWrapper: AnnotationWrapper, removeFromDictionary: boolean = false) {
|
||||
if (this._permissionsService.isManagerAndOwner()) {
|
||||
return this._manualRedactionControllerService
|
||||
.removeRedaction(
|
||||
{
|
||||
@ -164,10 +138,7 @@ export class ManualAnnotationService {
|
||||
tap(
|
||||
() => this._notify('manual-annotation.remove-redaction-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.remove-redaction-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.remove-redaction-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -186,10 +157,7 @@ export class ManualAnnotationService {
|
||||
tap(
|
||||
() => this._notify('manual-annotation.remove-redaction-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.remove-redaction-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.remove-redaction-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -198,19 +166,12 @@ export class ManualAnnotationService {
|
||||
|
||||
private _makeRedactionRequest(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._manualRedactionControllerService
|
||||
.requestAddRedaction(
|
||||
manualRedactionEntry,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.requestAddRedaction(manualRedactionEntry, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.redaction-request.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.redaction-request.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.redaction-request.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
@ -218,34 +179,23 @@ export class ManualAnnotationService {
|
||||
|
||||
private _makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
|
||||
return this._manualRedactionControllerService
|
||||
.addRedaction(
|
||||
manualRedactionEntry,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.addRedaction(manualRedactionEntry, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId)
|
||||
.pipe(
|
||||
tap(
|
||||
() => this._notify('manual-annotation.redaction-add.success'),
|
||||
() => {
|
||||
this._notify(
|
||||
'manual-annotation.redaction-add.error',
|
||||
NotificationType.ERROR
|
||||
);
|
||||
this._notify('manual-annotation.redaction-add.error', NotificationType.ERROR);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _notify(key: string, type: NotificationType = NotificationType.SUCCESS) {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(key),
|
||||
null,
|
||||
type
|
||||
);
|
||||
this._notificationService.showToastNotification(this._translateService.instant(key), null, type);
|
||||
}
|
||||
|
||||
getTitle(type: 'DICTIONARY' | 'REDACTION') {
|
||||
if (this._appStateService.isActiveProjectOwnerAndManager) {
|
||||
if (this._permissionsService.isManagerAndOwner()) {
|
||||
if (type === 'DICTIONARY') {
|
||||
return 'manual-redaction.dialog.header.dictionary';
|
||||
} else {
|
||||
|
||||
@ -55,7 +55,7 @@ export class ProjectListingScreenComponent implements OnInit {
|
||||
public ngOnInit(): void {
|
||||
this.appStateService.reset();
|
||||
this._calculateData();
|
||||
this.appStateService.fileStatusChanged.subscribe(() => {
|
||||
this.appStateService.fileChanged.subscribe(() => {
|
||||
this._calculateData();
|
||||
});
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ export class ProjectDetailsComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.calculateChartConfig();
|
||||
this.appStateService.fileStatusChanged.subscribe((event) => {
|
||||
this.appStateService.fileChanged.subscribe((event) => {
|
||||
this.calculateChartConfig();
|
||||
});
|
||||
}
|
||||
|
||||
@ -122,11 +122,7 @@
|
||||
<div [class.disabled]="fileStatus.isPending || fileStatus.isProcessing" [class.error]="fileStatus.isError" class="table-item-title">
|
||||
{{ fileStatus.filename }}
|
||||
</div>
|
||||
<span
|
||||
*ngIf="appStateService.fileNotUpToDateWithDictionary(fileStatus)"
|
||||
class="pill"
|
||||
translate="project-overview.new-rule.label"
|
||||
></span>
|
||||
<span *ngIf="permissionsService.fileRequiresReanalysis(fileStatus)" class="pill" translate="project-overview.new-rule.label"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -163,7 +159,11 @@
|
||||
></redaction-status-bar>
|
||||
|
||||
<div class="action-buttons">
|
||||
<redaction-file-actions [fileStatus]="fileStatus" (actionPerformed)="calculateData()"></redaction-file-actions>
|
||||
<redaction-file-actions
|
||||
[fileStatus]="fileStatus"
|
||||
(actionPerformed)="calculateData()"
|
||||
*ngIf="!fileStatus.isProcessing"
|
||||
></redaction-file-actions>
|
||||
<redaction-status-bar
|
||||
class="mr-8"
|
||||
*ngIf="fileStatus.isWorkable"
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FileUploadControllerService, ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { FileDropOverlayService } from '../../upload/file-drop/service/file-drop-overlay.service';
|
||||
@ -63,7 +62,7 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
this.appStateService.activateProject(params.projectId);
|
||||
});
|
||||
|
||||
this.appStateService.fileStatusChanged.subscribe(() => {
|
||||
this.appStateService.fileChanged.subscribe(() => {
|
||||
this.calculateData();
|
||||
});
|
||||
}
|
||||
@ -71,43 +70,45 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
|
||||
ngOnInit(): void {
|
||||
this._fileDropOverlayService.initFileDropHandling();
|
||||
this.calculateData();
|
||||
this._displayNewRuleToast();
|
||||
this._displayOutdatedToast();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this._fileDropOverlayService.cleanupFileDropHandling();
|
||||
}
|
||||
|
||||
private _displayNewRuleToast() {
|
||||
// @ts-ignore
|
||||
if (!this.appStateService.activeProject.files.filter((file) => this.appStateService.fileNotUpToDateWithDictionary(file)).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._notificationService.showToastNotification(
|
||||
`${this._translateService.instant('project-overview.new-rule.toast.message-project')} <span class="pill">${this._translateService.instant(
|
||||
'project-overview.new-rule.label'
|
||||
)}</span>`,
|
||||
null,
|
||||
NotificationType.WARNING,
|
||||
{
|
||||
disableTimeOut: true,
|
||||
positionClass: 'toast-top-left',
|
||||
actions: [
|
||||
{
|
||||
title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'),
|
||||
action: () =>
|
||||
this.appStateService
|
||||
.reanalyzeProject()
|
||||
.toPromise()
|
||||
.then(() => this.reloadProjects())
|
||||
},
|
||||
{
|
||||
title: this._translateService.instant('project-overview.new-rule.toast.actions.later')
|
||||
}
|
||||
]
|
||||
private _displayOutdatedToast() {
|
||||
if (this.permissionsService.isManagerAndOwner()) {
|
||||
// @ts-ignore
|
||||
if (!this.appStateService.activeProject.files.filter((file) => this.permissionsService.fileRequiresReanalysis(file)).length) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
this._notificationService.showToastNotification(
|
||||
`${this._translateService.instant('project-overview.new-rule.toast.message-project')} <span class="pill">${this._translateService.instant(
|
||||
'project-overview.new-rule.label'
|
||||
)}</span>`,
|
||||
null,
|
||||
NotificationType.WARNING,
|
||||
{
|
||||
disableTimeOut: true,
|
||||
positionClass: 'toast-top-left',
|
||||
actions: [
|
||||
{
|
||||
title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'),
|
||||
action: () =>
|
||||
this.appStateService
|
||||
.reanalyzeProject()
|
||||
.toPromise()
|
||||
.then(() => this.reloadProjects())
|
||||
},
|
||||
{
|
||||
title: this._translateService.instant('project-overview.new-rule.toast.actions.later')
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
reloadProjects() {
|
||||
|
||||
@ -39,7 +39,7 @@ export interface AppState {
|
||||
export class AppStateService {
|
||||
private _appState: AppState;
|
||||
private _dictionaryData: { [key: string]: TypeValue } = null;
|
||||
public fileStatusChanged = new EventEmitter<FileStatusWrapper>();
|
||||
public fileChanged = new EventEmitter<FileStatusWrapper>();
|
||||
public fileReanalysed = new EventEmitter<FileStatusWrapper>();
|
||||
|
||||
constructor(
|
||||
@ -52,8 +52,7 @@ export class AppStateService {
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _versionsControllerService: VersionsControllerService,
|
||||
private readonly _viewedPagesControllerService: ViewedPagesControllerService
|
||||
private readonly _versionsControllerService: VersionsControllerService
|
||||
) {
|
||||
this._appState = {
|
||||
projects: [],
|
||||
@ -98,10 +97,6 @@ export class AppStateService {
|
||||
return this._dictionaryData;
|
||||
}
|
||||
|
||||
getViewedPagesForActiveFile() {
|
||||
return this._viewedPagesControllerService.getViewedPages(this.activeProjectId, this.activeFileId);
|
||||
}
|
||||
|
||||
reanalyzeProject(project?: Project) {
|
||||
if (!project) {
|
||||
project = this.activeProject.project;
|
||||
@ -206,13 +201,12 @@ export class AppStateService {
|
||||
for (const oldFile of oldFiles) {
|
||||
if (oldFile.fileId === file.fileId) {
|
||||
// emit when analysis count changed
|
||||
if (oldFile.lastUpdated !== file.lastUpdated) {
|
||||
const fileStatusWrapper = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
|
||||
const fileStatusWrapper = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
|
||||
if (JSON.stringify(oldFile) !== JSON.stringify(fileStatusWrapper)) {
|
||||
fileStatusChangedEvent.push(fileStatusWrapper);
|
||||
|
||||
if (oldFile.lastProcessed !== file.lastProcessed) {
|
||||
fileReanalysedEvent.push(fileStatusWrapper);
|
||||
}
|
||||
}
|
||||
if (oldFile.lastProcessed !== file.lastProcessed) {
|
||||
fileReanalysedEvent.push(fileStatusWrapper);
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
@ -230,7 +224,7 @@ export class AppStateService {
|
||||
this._computeStats();
|
||||
|
||||
fileReanalysedEvent.forEach((file) => this.fileReanalysed.emit(file));
|
||||
fileStatusChangedEvent.forEach((file) => this.fileStatusChanged.emit(file));
|
||||
fileStatusChangedEvent.forEach((file) => this.fileChanged.emit(file));
|
||||
|
||||
return files;
|
||||
}
|
||||
@ -429,16 +423,6 @@ export class AppStateService {
|
||||
}
|
||||
}
|
||||
|
||||
fileNotUpToDateWithDictionary(fileStatus?: FileStatusWrapper) {
|
||||
if (!fileStatus) {
|
||||
fileStatus = this.activeFile;
|
||||
}
|
||||
return (
|
||||
(fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') &&
|
||||
(fileStatus.dictionaryVersion !== this.dictionaryVersion || fileStatus.rulesVersion !== this.rulesVersion)
|
||||
);
|
||||
}
|
||||
|
||||
async updateDictionaryVersion() {
|
||||
const result = await this._versionsControllerService.getVersions().toPromise();
|
||||
this._appState.dictionaryVersion = result.dictionaryVersion;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user