Pull request #224: Exclude pages from redaction

Merge in RED/ui from RED-1618 to master

* commit '751ccb5ef4c844f61bcb777af5f5783b297da9bc':
  Exclude pages icon
  Exclude pages from redaction
  Fixed comments params order
This commit is contained in:
Adina Teudan 2021-06-29 09:10:34 +02:00
commit 2767809cd2
23 changed files with 537 additions and 199 deletions

View File

@ -196,4 +196,8 @@ export class FileStatusWrapper {
get cacheIdentifier() {
return btoa(this.fileStatus.lastUploaded + this.fileStatus.lastOCRTime);
}
get excludedPages(): number[] {
return this.fileStatus.excludedPages;
}
}

View File

@ -23,7 +23,8 @@
*ngIf="permissionsService.canAddComment()"
[form]="commentForm"
[placeholder]="translateService.instant('comments.add-comment')"
type="submit"
icon="red:collapse"
type="action"
width="full"
></redaction-input-with-action>

View File

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, HostBinding, Input } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Comment } from '@redaction/red-ui-http';
import { ManualAnnotationService } from '../../services/manual-annotation.service';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -28,7 +28,7 @@ export class CommentsComponent {
private readonly _manualAnnotationService: ManualAnnotationService
) {
this.commentForm = this._formBuilder.group({
value: ['', Validators.required]
value: ['']
});
}

View File

@ -58,6 +58,15 @@
tooltipPosition="below"
></redaction-circle-button>
<redaction-circle-button
(action)="toggleExcludePages()"
*ngIf="screen === 'file-preview'"
[attr.aria-expanded]="activeExcludePages"
icon="red:exclude-pages"
tooltip="file-preview.exclude-pages"
tooltipPosition="below"
></redaction-circle-button>
<!-- Ready for approval-->
<redaction-circle-button
(action)="setFileUnderApproval($event)"
@ -136,8 +145,8 @@
<!-- exclude from redaction -->
<div class="red-input-group">
<mat-slide-toggle
(click)="$event.stopPropagation()"
(change)="toggleAnalysis()"
(click)="$event.stopPropagation()"
[checked]="!fileStatus?.isExcluded"
[class.mr-24]="screen === 'dossier-overview'"
[disabled]="!permissionsService.canToggleAnalysis(fileStatus)"

View File

@ -13,6 +13,7 @@ import { DossiersDialogService } from '../../services/dossiers-dialog.service';
export class FileActionsComponent implements OnInit {
@Input() fileStatus: FileStatusWrapper;
@Input() activeDocumentInfo: boolean;
@Input() activeExcludePages: boolean;
@Output() actionPerformed = new EventEmitter<string>();
actionMenuOpen: boolean;
@ -104,6 +105,10 @@ export class FileActionsComponent implements OnInit {
this.actionPerformed.emit('view-document-info');
}
toggleExcludePages() {
this.actionPerformed.emit('view-exclude-pages');
}
openDeleteFileDialog($event: MouseEvent) {
this._dialogService.openDeleteFilesDialog(
$event,

View File

@ -1,4 +1,8 @@
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
<div
*ngIf="!excludePages"
class="right-title heading"
translate="file-preview.tabs.annotations.label"
>
<div>
<div
(click)="multiSelectActive = true"
@ -16,14 +20,28 @@
></redaction-popup-filter>
</div>
</div>
<div
*ngIf="excludePages"
class="right-title heading"
translate="file-preview.tabs.exclude-pages.label"
>
<div>
<redaction-circle-button
(action)="closeExcludePagesView.emit()"
icon="red:close"
tooltip="file-preview.tabs.exclude-pages.close"
tooltipPosition="before"
></redaction-circle-button>
</div>
</div>
<div class="right-content">
<div class="read-only d-flex" *ngIf="isReadOnly" [class.justify-center]="!isProcessing">
<div class="flex-align-items-center" *ngIf="isProcessing">
<span class="read-only-text" [translate]="fileData.fileStatus.status"></span>
<mat-progress-bar class="w-100" [mode]="'indeterminate'"></mat-progress-bar>
<div *ngIf="isReadOnly" [class.justify-center]="!isProcessing" class="read-only d-flex">
<div *ngIf="isProcessing" class="flex-align-items-center">
<span [translate]="fileData.fileStatus.status" class="read-only-text"></span>
<mat-progress-bar [mode]="'indeterminate'" class="w-100"></mat-progress-bar>
</div>
<div class="flex-center">
<mat-icon svgIcon="red:read-only" class="red-white"></mat-icon>
<mat-icon class="red-white" svgIcon="red:read-only"></mat-icon>
<span class="read-only-text" translate="readonly"></span>
</div>
</div>
@ -89,133 +107,150 @@
</div>
<div style="overflow: hidden; width: 100%">
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
<span *ngIf="!!activeViewerPage" class="all-caps-label">
<span translate="page"></span> {{ activeViewerPage }} -
{{ activeAnnotationsLength || 0 }}
<span
[translate]="activeAnnotationsLength === 1 ? 'annotation' : 'annotations'"
></span>
</span>
<ng-container *ngIf="!excludePages">
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
<span *ngIf="!!activeViewerPage" class="all-caps-label">
<span translate="page"></span> {{ activeViewerPage }} -
{{ activeAnnotationsLength || 0 }}
<span
[translate]="
activeAnnotationsLength === 1 ? 'annotation' : 'annotations'
"
></span>
</span>
<div *ngIf="multiSelectActive">
<div (click)="selectAllOnActivePage()" class="all-caps-label primary pointer">
All
</div>
<div (click)="deselectAllOnActivePage()" class="all-caps-label primary pointer">
None
<div *ngIf="multiSelectActive">
<div
(click)="selectAllOnActivePage()"
class="all-caps-label primary pointer"
translate="file-preview.tabs.annotations.select-all"
></div>
<div
(click)="deselectAllOnActivePage()"
class="all-caps-label primary pointer"
translate="file-preview.tabs.annotations.select-none"
></div>
</div>
</div>
</div>
<div
#annotationsElement
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
class="annotations"
redactionHasScrollbar
tabindex="1"
>
<ng-container *ngIf="!displayedAnnotations[activeViewerPage]">
<redaction-empty-state
[horizontalPadding]="24"
[verticalPadding]="40"
icon="red:document"
screen="file-preview"
></redaction-empty-state>
<div class="no-annotations-buttons-container mt-32">
<redaction-icon-button
(action)="jumpToPreviousWithAnnotations()"
[disabled]="activeViewerPage <= displayedPages[0]"
icon="red:nav-prev"
text="file-preview.tabs.annotations.jump-to-previous"
type="show-bg"
></redaction-icon-button>
<redaction-icon-button
(action)="jumpToNextWithAnnotations()"
[disabled]="
activeViewerPage >= displayedPages[displayedPages.length - 1]
"
class="mt-8"
icon="red:nav-next"
text="file-preview.tabs.annotations.jump-to-next"
type="show-bg"
></redaction-icon-button>
</div>
</ng-container>
<div
(click)="annotationClicked(annotation, $event)"
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
[class.active]="annotationIsSelected(annotation)"
[class.multi-select-active]="multiSelectActive"
attr.annotation-id="{{ annotation.id }}"
attr.annotation-page="{{ activeViewerPage }}"
class="annotation-wrapper"
#annotationsElement
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
class="annotations"
redactionHasScrollbar
tabindex="1"
>
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
<redaction-hidden-action
(action)="logAnnotation(annotation)"
[requiredClicks]="2"
>
<div
[matTooltip]="annotation.content"
class="details"
matTooltipPosition="above"
<ng-container *ngIf="!displayedAnnotations[activeViewerPage]">
<redaction-empty-state
[horizontalPadding]="24"
[verticalPadding]="40"
icon="red:document"
screen="file-preview"
></redaction-empty-state>
<div class="no-annotations-buttons-container mt-32">
<redaction-icon-button
(action)="jumpToPreviousWithAnnotations()"
[disabled]="activeViewerPage <= displayedPages[0]"
icon="red:nav-prev"
text="file-preview.tabs.annotations.jump-to-previous"
type="show-bg"
></redaction-icon-button>
<redaction-icon-button
(action)="jumpToNextWithAnnotations()"
[disabled]="
activeViewerPage >= displayedPages[displayedPages.length - 1]
"
class="mt-8"
icon="red:nav-next"
text="file-preview.tabs.annotations.jump-to-next"
type="show-bg"
></redaction-icon-button>
</div>
</ng-container>
<div
(click)="annotationClicked(annotation, $event)"
*ngFor="
let annotation of displayedAnnotations[activeViewerPage]?.annotations
"
[class.active]="annotationIsSelected(annotation)"
[class.multi-select-active]="multiSelectActive"
attr.annotation-id="{{ annotation.id }}"
attr.annotation-page="{{ activeViewerPage }}"
class="annotation-wrapper"
>
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
<redaction-hidden-action
(action)="logAnnotation(annotation)"
[requiredClicks]="2"
>
<redaction-type-annotation-icon
[annotation]="annotation"
></redaction-type-annotation-icon>
<div class="flex-1">
<div>
<strong>{{ annotation.typeLabel | translate }}</strong>
</div>
<div *ngIf="annotation?.dictionary !== 'manual'">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.dictionary | humanize: false }}
</div>
<div *ngIf="annotation.shortContent && !annotation.isHint">
<strong><span translate="content"></span>: </strong
>{{ annotation.shortContent }}
</div>
</div>
<div class="active-icon-marker-container">
<redaction-round-checkbox
*ngIf="
multiSelectActive && annotationIsSelected(annotation)
"
[active]="true"
></redaction-round-checkbox>
</div>
</div>
<div class="actions-wrapper">
<div
(click)="toggleExpandComments(annotation, $event)"
[matTooltip]="commentsTooltip(annotation)"
class="comments-counter"
[matTooltip]="annotation.content"
class="details"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.comments.length }}
<redaction-type-annotation-icon
[annotation]="annotation"
></redaction-type-annotation-icon>
<div class="flex-1">
<div>
<strong>{{ annotation.typeLabel | translate }}</strong>
</div>
<div *ngIf="annotation?.dictionary !== 'manual'">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.dictionary | humanize: false }}
</div>
<div *ngIf="annotation.shortContent && !annotation.isHint">
<strong><span translate="content"></span>: </strong
>{{ annotation.shortContent }}
</div>
</div>
<div class="active-icon-marker-container">
<redaction-round-checkbox
*ngIf="
multiSelectActive &&
annotationIsSelected(annotation)
"
[active]="true"
></redaction-round-checkbox>
</div>
</div>
<div class="actions" *ngIf="!multiSelectActive">
<ng-container
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
></ng-container>
<div class="actions-wrapper">
<div
(click)="toggleExpandComments(annotation, $event)"
[matTooltip]="commentsTooltip(annotation)"
class="comments-counter"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.comments.length }}
</div>
<div *ngIf="!multiSelectActive" class="actions">
<ng-container
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
></ng-container>
</div>
</div>
</div>
<redaction-comments [annotation]="annotation"></redaction-comments>
</redaction-hidden-action>
<redaction-comments [annotation]="annotation"></redaction-comments>
</redaction-hidden-action>
</div>
</div>
</div>
</div>
</ng-container>
<redaction-page-exclusion
(actionPerformed)="actionPerformed.emit($event)"
*ngIf="excludePages"
[fileData]="fileData"
></redaction-page-exclusion>
</div>
</div>
</div>

View File

@ -233,7 +233,7 @@
}
&:hover {
background-color: #f9fafb;
background-color: $grey-8;
::ng-deep .annotation-actions {
display: flex;

View File

@ -40,6 +40,7 @@ export class FileWorkloadComponent {
@Input() secondaryFilters: FilterModel[];
@Input() fileData: FileDataModel;
@Input() hideSkipped: boolean;
@Input() excludePages: boolean;
@Input() annotationActionsTemplate: TemplateRef<any>;
@Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
@Output() selectAnnotations = new EventEmitter<
@ -49,6 +50,8 @@ export class FileWorkloadComponent {
@Output() selectPage = new EventEmitter<number>();
@Output() toggleSkipped = new EventEmitter<any>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
@Output() closeExcludePagesView = new EventEmitter();
@Output() actionPerformed = new EventEmitter<string>();
displayedPages: number[] = [];
pagesPanelActive = true;
@ViewChildren(CommentsComponent) annotationCommentsComponents: QueryList<CommentsComponent>;
@ -56,9 +59,9 @@ export class FileWorkloadComponent {
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
constructor(
private readonly _permissionsService: PermissionsService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _annotationProcessingService: AnnotationProcessingService,
private readonly _permissionsService: PermissionsService,
private readonly _translateService: TranslateService
) {}
@ -71,18 +74,6 @@ export class FileWorkloadComponent {
private _multiSelectActive = false;
get isProcessing(): boolean {
return this.fileData.fileStatus?.isProcessing;
}
get activeAnnotationsLength(): number | undefined {
return this.displayedAnnotations[this.activeViewerPage]?.annotations?.length;
}
get isReadOnly(): boolean {
return !this._permissionsService.canPerformAnnotationActions();
}
get multiSelectActive(): boolean {
return this._multiSelectActive;
}
@ -97,6 +88,18 @@ export class FileWorkloadComponent {
}
}
get isProcessing(): boolean {
return this.fileData?.fileStatus?.isProcessing;
}
get activeAnnotationsLength(): number | undefined {
return this.displayedAnnotations[this.activeViewerPage]?.annotations?.length;
}
get isReadOnly(): boolean {
return !this._permissionsService.canPerformAnnotationActions();
}
private get _firstSelectedAnnotation() {
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
}
@ -224,15 +227,21 @@ export class FileWorkloadComponent {
}
scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed') {
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(
`div[anotation-page-header="${page}"]`
);
FileWorkloadComponent._scrollToFirstElement(elements, mode);
if (this._annotationsElement) {
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(
`div[anotation-page-header="${page}"]`
);
FileWorkloadComponent._scrollToFirstElement(elements, mode);
}
}
@debounce()
scrollToSelectedAnnotation() {
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
if (
!this.selectedAnnotations ||
this.selectedAnnotations.length === 0 ||
!this._annotationsElement
) {
return;
}
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(
@ -420,9 +429,11 @@ export class FileWorkloadComponent {
}
private _scrollQuickNavigationToPage(page: number) {
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(
`#quick-nav-page-${page}`
);
FileWorkloadComponent._scrollToFirstElement(elements);
if (this._quickNavigationElement) {
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(
`#quick-nav-page-${page}`
);
FileWorkloadComponent._scrollToFirstElement(elements);
}
}
}

View File

@ -0,0 +1,41 @@
<div *ngIf="permissionsService.canExcludePages()" class="exclude-pages-input-container">
<redaction-input-with-action
(action)="excludePagesRange()"
[form]="excludePagesForm"
hint="file-preview.tabs.exclude-pages.hint"
icon="red:check"
placeholder="file-preview.tabs.exclude-pages.input-placeholder"
type="action"
width="full"
></redaction-input-with-action>
</div>
<div *ngIf="excludedPagesRanges.length" class="all-caps-label-container">
<div
class="all-caps-label"
translate="file-preview.tabs.exclude-pages.removed-from-redaction"
></div>
</div>
<div class="ranges" redactionHasScrollbar>
<div *ngFor="let range of excludedPagesRanges" class="range">
<ng-container *ngIf="range.startPage === range.endPage">
{{ range.startPage }}
</ng-container>
<ng-container *ngIf="range.startPage !== range.endPage">
{{ range.startPage }}-{{ range.endPage }}
</ng-container>
<redaction-circle-button
(action)="includePagesRange(range)"
*ngIf="permissionsService.canExcludePages()"
icon="red:undo"
tooltip="file-preview.tabs.exclude-pages.put-back"
></redaction-circle-button>
</div>
</div>
<div
*ngIf="!permissionsService.canExcludePages() && !excludedPagesRanges.length"
class="no-excluded heading-l"
translate="file-preview.tabs.exclude-pages.no-excluded"
></div>

View File

@ -0,0 +1,60 @@
@import '../../../../../assets/styles/red-variables';
@import '../../../../../assets/styles/red-mixins';
:host {
height: 100%;
display: flex;
flex-direction: column;
.exclude-pages-input-container {
background-color: $grey-6;
padding: 15px 15px 16px 14px;
}
.all-caps-label-container {
padding: 8px 16px;
border-bottom: 1px solid $separator;
}
.ranges {
overflow: hidden;
.range {
padding-left: 17px;
padding-right: 16px;
border-bottom: 1px solid $separator;
transition: background-color 0.2s;
display: flex;
align-items: center;
justify-content: space-between;
height: 50px;
redaction-circle-button {
display: none;
}
&:hover {
background-color: $grey-8;
redaction-circle-button {
display: initial;
}
}
}
&.has-scrollbar:hover {
@include scroll-bar;
overflow: auto;
.range {
padding-right: 5px;
}
}
}
.no-excluded {
padding: 50px 16px;
text-align: center;
opacity: 0.7;
}
}

View File

@ -0,0 +1,108 @@
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { PermissionsService } from '../../../../services/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { PageRange, ReanalysisControllerService } from '@redaction/red-ui-http';
import { FileDataModel } from '../../../../models/file/file-data.model';
import { NotificationService, NotificationType } from '../../../../services/notification.service';
import { LoadingService } from '../../../../services/loading.service';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'redaction-page-exclusion',
templateUrl: './page-exclusion.component.html',
styleUrls: ['./page-exclusion.component.scss']
})
export class PageExclusionComponent implements OnChanges {
@Input() fileData: FileDataModel;
@Output() actionPerformed = new EventEmitter<string>();
excludePagesForm: FormGroup;
excludedPagesRanges: PageRange[] = [];
constructor(
readonly permissionsService: PermissionsService,
private readonly _formBuilder: FormBuilder,
private readonly _reanalysisControllerService: ReanalysisControllerService,
private readonly _notificationService: NotificationService,
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService
) {
this.excludePagesForm = this._formBuilder.group({
value: ['']
});
}
ngOnChanges(changes: SimpleChanges) {
if (changes.fileData) {
const excludedPages = (this.fileData?.fileStatus?.excludedPages || []).sort(
(p1, p2) => p1 - p2
);
this.excludedPagesRanges = excludedPages.reduce((ranges, page) => {
if (!ranges.length) {
return [{ startPage: page, endPage: page }];
}
if (page === ranges[ranges.length - 1].endPage + 1) {
ranges[ranges.length - 1].endPage = page;
} else {
ranges.push({ startPage: page, endPage: page });
}
return ranges;
}, []);
}
}
async excludePagesRange() {
this._loadingService.start();
try {
const pageRanges = this.excludePagesForm
.get('value')
.value.split(',')
.map(range => {
const splitted = range.split('-');
const startPage = parseInt(splitted[0], 10);
const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage;
if (!startPage || !endPage) {
throw new Error();
}
return {
startPage,
endPage
};
});
await this._reanalysisControllerService
.excludePages(
{
pageRanges: pageRanges
},
this.fileData.fileStatus.dossierId,
this.fileData.fileStatus.fileId
)
.toPromise();
this.excludePagesForm.reset();
this.actionPerformed.emit('exclude-pages');
} catch (e) {
this._notificationService.showToastNotification(
this._translateService.instant('file-preview.tabs.exclude-pages.error'),
null,
NotificationType.ERROR
);
this._loadingService.stop();
}
}
async includePagesRange(range: PageRange) {
this._loadingService.start();
await this._reanalysisControllerService
.includePages(
{
pageRanges: [range]
},
this.fileData.fileStatus.dossierId,
this.fileData.fileStatus.fileId
)
.toPromise();
this.excludePagesForm.reset();
this.actionPerformed.emit('exclude-pages');
}
}

View File

@ -45,6 +45,7 @@ import { EditDossierTeamMembersComponent } from './dialogs/edit-dossier-dialog/t
import { TeamMembersManagerComponent } from './components/team-members-manager/team-members-manager.component';
import { TeamMembersDialogComponent } from './dialogs/team-members-dialog/team-members-dialog.component';
import { ScrollButtonComponent } from './components/scroll-button/scroll-button.component';
import { PageExclusionComponent } from './components/page-exclusion/page-exclusion.component';
const screens = [
DossierListingScreenComponent,
@ -87,6 +88,7 @@ const components = [
EditDossierTeamMembersComponent,
TeamMembersManagerComponent,
ScrollButtonComponent,
PageExclusionComponent,
...screens,
...dialogs

View File

@ -56,11 +56,11 @@
</ng-container>
<redaction-assign-user-dropdown
*ngIf="editingReviewer"
[value]="currentReviewer"
[options]="singleUsersSelectOptions"
(save)="assignReviewer($event)"
(cancel)="editingReviewer = false"
(save)="assignReviewer($event)"
*ngIf="editingReviewer"
[options]="singleUsersSelectOptions"
[value]="currentReviewer"
></redaction-assign-user-dropdown>
<div *ngIf="canAssign" class="assign-actions-wrapper">
@ -95,6 +95,7 @@
(actionPerformed)="fileActionPerformed($event)"
*ngIf="viewReady"
[activeDocumentInfo]="viewDocumentInfo"
[activeExcludePages]="excludePages"
></redaction-file-actions>
<redaction-circle-button
@ -152,7 +153,12 @@
<div class="right-container">
<redaction-empty-state
*ngIf="viewReady && appStateService.activeFile.isExcluded && !viewDocumentInfo"
*ngIf="
viewReady &&
appStateService.activeFile.isExcluded &&
!viewDocumentInfo &&
!excludePages
"
[horizontalPadding]="40"
icon="red:needs-work"
text="file-preview.tabs.is-excluded"
@ -166,7 +172,9 @@
<redaction-file-workload
#fileWorkloadComponent
(actionPerformed)="fileActionPerformed($event)"
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(closeExcludePagesView)="excludePages = false"
(deselectAnnotations)="deselectAnnotations($event)"
(selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)"
@ -177,6 +185,7 @@
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="annotations"
[dialogRef]="dialogRef"
[excludePages]="excludePages"
[fileData]="fileData"
[hideSkipped]="hideSkipped"
[primaryFilters]="primaryFilters"

View File

@ -45,6 +45,7 @@ import {
handleFilterDelta,
processFilters
} from '@shared/components/filters/popup-filter/utils/filter-utils';
import { LoadingService } from '../../../../services/loading.service';
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@ -71,6 +72,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
hideSkipped = false;
displayPDFViewer = false;
viewDocumentInfo = false;
excludePages = false;
private _instance: WebViewerInstance;
private _lastPage: string;
@ -94,7 +96,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private readonly _fileDownloadService: PdfViewerDataService,
private readonly _statusControllerService: StatusControllerService,
private readonly _ngZone: NgZone,
private readonly _fileManagementControllerService: FileManagementControllerService
private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _loadingService: LoadingService
) {
document.documentElement.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
@ -169,6 +172,36 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
return this.appStateService.activeFile.fileStatus.lastReviewer;
}
get assignOrChangeReviewerTooltip() {
return this.currentReviewer
? 'file-preview.change-reviewer'
: 'file-preview.assign-reviewer';
}
get currentReviewer(): string {
return this.appStateService.activeFile.currentReviewer;
}
get status(): FileStatus.StatusEnum {
return this.appStateService.activeFile.status;
}
get statusBarConfig(): [{ length: number; color: FileStatus.StatusEnum }] {
return [{ length: 1, color: this.status }];
}
get isUnderReviewOrApproval(): boolean {
return this.status === 'UNDER_REVIEW' || this.status === 'UNDER_APPROVAL';
}
get canAssignReviewer(): boolean {
return (
!this.currentReviewer &&
this.permissionsService.canAssignUser() &&
this.appStateService.activeDossier.hasMoreThanOneReviewer
);
}
updateViewMode() {
const annotations = this._getAnnotations(a => a.getCustomData('redacto-manager'));
const redactions = annotations.filter(a => a.getCustomData('redaction'));
@ -441,10 +474,22 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
await this.appStateService.reloadActiveDossierFiles();
return;
case 'exclude-pages':
await this.appStateService.reloadActiveDossierFiles();
await this._loadFileData();
this._loadingService.stop();
return;
case 'view-document-info':
this.viewDocumentInfo = !this.viewDocumentInfo;
return;
case 'view-exclude-pages':
this.excludePages = !this.excludePages;
this._workloadComponent.multiSelectActive = false;
this.viewDocumentInfo = false;
return;
default:
this._updateCanPerformActions();
}
@ -509,36 +554,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
return false;
}
get assignOrChangeReviewerTooltip() {
return this.currentReviewer
? 'file-preview.change-reviewer'
: 'file-preview.assign-reviewer';
}
get currentReviewer(): string {
return this.appStateService.activeFile.currentReviewer;
}
get status(): FileStatus.StatusEnum {
return this.appStateService.activeFile.status;
}
get statusBarConfig(): [{ length: number; color: FileStatus.StatusEnum }] {
return [{ length: 1, color: this.status }];
}
get isUnderReviewOrApproval(): boolean {
return this.status === 'UNDER_REVIEW' || this.status === 'UNDER_APPROVAL';
}
get canAssignReviewer(): boolean {
return (
!this.currentReviewer &&
this.permissionsService.canAssignUser() &&
this.appStateService.activeDossier.hasMoreThanOneReviewer
);
}
// <!-- Dev Mode Features-->
async openSSRFilePreview() {
window.open(`/pdf-preview/${this.dossierId}/${this.fileId}`, '_blank');
@ -576,7 +591,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
private async _loadFileData(performUpdate: boolean = false): Promise<void> {
const fileData = await this._fileDownloadService.loadActiveFileData().toPromise();
console.log(fileData);
if (!fileData.fileStatus.isPending && !fileData.fileStatus.isError) {
if (performUpdate) {
this.fileData.redactionLog = fileData.redactionLog;

View File

@ -30,19 +30,19 @@ export class ManualAnnotationService {
addComment(comment: string, annotationId: string) {
return this._manualRedactionControllerService.addComment(
{ text: comment },
annotationId,
this._appStateService.activeDossierId,
this._appStateService.activeFileId,
annotationId
this._appStateService.activeFileId
);
}
// this wraps /manualRedaction/comment/undo
deleteComment(commentId: string, annotationId: string) {
return this._manualRedactionControllerService.undoComment(
this._appStateService.activeDossierId,
this._appStateService.activeFileId,
annotationId,
commentId
commentId,
this._appStateService.activeDossierId,
this._appStateService.activeFileId
);
}

View File

@ -39,6 +39,7 @@ export class IconsModule {
'edit',
'entries',
'error',
'exclude-pages',
'exit-fullscreen',
'expand',
'folder',

View File

@ -1,4 +1,4 @@
<form (submit)="type === 'submit' && submit()" [formGroup]="form">
<form [formGroup]="form">
<div [style.max-width]="computedWidth" [style.width]="computedWidth" class="red-input-group">
<input
[formControlName]="formControlName"
@ -8,6 +8,8 @@
type="text"
/>
<span *ngIf="hint" [translate]="hint" class="hint"></span>
<!-- Search-->
<mat-icon
*ngIf="type === 'search' && !hasContent"
@ -26,11 +28,11 @@
<!-- Submit-->
<redaction-circle-button
(action)="submit($event)"
*ngIf="type === 'submit'"
[disabled]="form.invalid"
(action)="executeAction($event)"
*ngIf="type === 'action'"
[disabled]="!hasContent"
[icon]="icon"
[size]="25"
icon="red:collapse"
>
</redaction-circle-button>
</div>

View File

@ -9,6 +9,6 @@ mat-icon.disabled {
redaction-circle-button {
position: absolute;
bottom: 5px;
top: 4px;
right: 5px;
}

View File

@ -9,8 +9,10 @@ import { FormGroup } from '@angular/forms';
export class InputWithActionComponent {
@Input() form: FormGroup;
@Input() placeholder: string;
@Input() hint: string;
@Input() width: number | 'full' = 250;
@Input() type: 'search' | 'submit';
@Input() type: 'search' | 'action';
@Input() icon: string;
@Output() action = new EventEmitter<any>();
get formControlName(): 'query' | 'value' {
@ -18,7 +20,7 @@ export class InputWithActionComponent {
}
get hasContent() {
return !!this.form.get(this.formControlName).value.length;
return !!this.form.get(this.formControlName).value?.length;
}
get computedWidth() {
@ -29,7 +31,7 @@ export class InputWithActionComponent {
this.form.patchValue({ query: '' });
}
submit($event?: MouseEvent) {
executeAction($event?: MouseEvent) {
$event?.stopPropagation();
if (this.hasContent) {
this.action.emit();

View File

@ -216,4 +216,11 @@ export class PermissionsService {
canAddComment(fileStatus = this._activeFile): boolean {
return this.isFileReviewer(fileStatus) || this.isApprover();
}
canExcludePages(fileStatus = this._activeFile): boolean {
return (
['UNDER_REVIEW', 'UNDER_APPROVAL'].includes(fileStatus.status) &&
(this.isFileReviewer(fileStatus) || this.isApprover())
);
}
}

View File

@ -375,7 +375,19 @@
"label": "Workload",
"select": "Select",
"jump-to-previous": "Jump to Previous",
"jump-to-next": "Jump to Next"
"jump-to-next": "Jump to Next",
"select-all": "All",
"select-none": "None"
},
"exclude-pages": {
"label": "Exclude Pages",
"close": "Close",
"error": "Error! Invalid page selection.",
"input-placeholder": "e.g. 1-20,22,32",
"hint": "Minus (-) for range and comma (,) for enumeration.",
"removed-from-redaction": "Removed from redaction",
"put-back": "Put back",
"no-excluded": "No excluded pages."
},
"is-excluded": "Redaction is disabled for this document."
},
@ -392,6 +404,7 @@
"last-reviewer": "Last Reviewed by:",
"fullscreen": "Full Screen (F)",
"document-info": "Your Document Info lives here. This includes metadata required on each document.",
"exclude-pages": "Exclude pages from redaction",
"new-tab-ssr": "Open Document in Server Side Rendering Mode",
"html-debug": "Open Document HTML Debug",
"download-original-file": "Download Original File",

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="14px" version="1.1" viewBox="0 0 14 14" width="14px"
xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" id="Styleguide" stroke="none" stroke-width="1">
<g fill="currentColor" fill-rule="nonzero"
id="Export-just-icons" transform="translate(-346.000000, -902.000000)">
<g id="status" transform="translate(346.000000, 902.000000)">
<path
d="M14,0 L14,11.2 L11.2,11.2 L11.2,14 L0,14 L0,2.8 L2.8,2.8 L2.8,0 L14,0 Z M2.8,4.2 L1.4,4.2 L1.4,12.6 L9.8,12.6 L9.8,11.2 L2.8,11.2 L2.8,4.2 Z M12.6,1.4 L4.2,1.4 L4.2,9.8 L12.6,9.8 L12.6,1.4 Z M9.88492424,3.12512627 L10.8748737,4.11507576 L9.39,5.6 L10.8748737,7.08492424 L9.88492424,8.07487373 L8.4,6.59 L6.91507576,8.07487373 L5.92512627,7.08492424 L7.41,5.6 L5.92512627,4.11507576 L6.91507576,3.12512627 L8.4,4.61 L9.88492424,3.12512627 Z"
id="pages"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@ -106,7 +106,7 @@ cdk-virtual-scroll-viewport {
&:hover {
> div {
background-color: #f9fafb;
background-color: $grey-8;
&.selection-column redaction-round-checkbox {
opacity: 1;