Document info

This commit is contained in:
Adina Țeudan 2021-03-23 23:45:43 +02:00
parent f0056fee56
commit 9659cbe289
24 changed files with 402 additions and 143 deletions

View File

@ -118,6 +118,8 @@ import { SearchInputComponent } from './components/search-input/search-input.com
import { AppRoutingModule } from './app-routing.module';
import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
import { ConfirmDeleteFileAttributeDialogComponent } from './dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component';
import { DocumentInfoComponent } from './components/document-info/document-info.component';
export function HttpLoaderFactory(httpClient: HttpClient) {
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
@ -222,7 +224,9 @@ const matImports = [
FileAttributesListingScreenComponent,
SearchInputComponent,
AddEditFileAttributeDialogComponent,
ConfirmDeleteFileAttributeDialogComponent
ConfirmDeleteFileAttributeDialogComponent,
DocumentInfoDialogComponent,
DocumentInfoComponent
],
imports: [
BrowserModule,

View File

@ -0,0 +1,39 @@
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
<div>
<redaction-circle-button
icon="red:edit"
(action)="closeDocumentInfoView.emit()"
tooltipPosition="before"
tooltip="file-preview.tabs.document-info.edit"
></redaction-circle-button>
<redaction-circle-button
icon="red:close"
(action)="closeDocumentInfoView.emit()"
tooltipPosition="before"
tooltip="file-preview.tabs.document-info.close"
></redaction-circle-button>
</div>
</div>
<div class="section">
Attributes go here
</div>
<div class="section small-label stats-subtitle">
<div>
<mat-icon svgIcon="red:folder"></mat-icon>
<span>{{ 'file-preview.tabs.document-info.details.project' | translate: { projectName: project.name } }}</span>
</div>
<div>
<mat-icon svgIcon="red:document"></mat-icon>
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate: { pages: file.numberOfPages } }}</span>
</div>
<div>
<mat-icon svgIcon="red:calendar"></mat-icon>
<span>{{ 'file-preview.tabs.document-info.details.created-on' | translate: { date: file.added | date: 'mediumDate' } }}</span>
</div>
<div *ngIf="project.project.dueDate">
<mat-icon svgIcon="red:lightning"></mat-icon>
<span>{{ 'file-preview.tabs.document-info.details.due' | translate: { date: project.project.dueDate | date: 'mediumDate' } }}</span>
</div>
</div>

View File

@ -0,0 +1,22 @@
@import '../../../assets/styles/red-variables';
.right-title > div {
display: flex;
> redaction-circle-button:not(:last-child) {
margin-right: 2px;
}
}
.section {
padding: 25px;
flex-direction: column;
> div {
justify-content: flex-start;
}
&:not(:last-child) {
border-bottom: 1px solid $separator;
}
}

View File

@ -0,0 +1,21 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FileStatus } from '@redaction/red-ui-http';
import { AppStateService } from '../../state/app-state.service';
@Component({
selector: 'redaction-document-info',
templateUrl: './document-info.component.html',
styleUrls: ['./document-info.component.scss']
})
export class DocumentInfoComponent implements OnInit {
@Input() file: FileStatus;
@Output() closeDocumentInfoView = new EventEmitter();
constructor(private _appStateService: AppStateService) {}
ngOnInit(): void {}
public get project() {
return this._appStateService.getProjectById(this.file.projectId);
}
}

View File

@ -49,6 +49,15 @@
>
</redaction-file-download-btn>
<redaction-circle-button
*ngIf="screen === 'file-preview'"
(action)="toggleViewDocumentInfo()"
tooltip="file-preview.document-info"
tooltipPosition="before"
icon="red:status-info"
[attr.aria-expanded]="activeDocumentInfo"
></redaction-circle-button>
<!-- Ready for approval-->
<redaction-circle-button
(action)="setFileUnderApproval($event, fileStatus)"

View File

@ -14,6 +14,7 @@ import { DEFAULT_RUL_SET_UUID } from '../../utils/rule-set-default';
})
export class FileActionsComponent implements OnInit {
@Input() fileStatus: FileStatusWrapper;
@Input() activeDocumentInfo: boolean;
@Output() actionPerformed = new EventEmitter<string>();
actionMenuOpen: boolean;
@ -40,6 +41,10 @@ export class FileActionsComponent implements OnInit {
}
}
public toggleViewDocumentInfo() {
this.actionPerformed.emit('view-document-info');
}
public get tooltipPosition() {
return this.screen === 'file-preview' ? 'below' : 'above';
}

View File

@ -1,13 +1,13 @@
<section class="dialog">
<div class="dialog-header heading-l">
{{ (fileAttribute ? 'add-edit-file-attribute.title.edit' : 'add-edit-file-attribute.title.new') | translate: { name: fileAttribute?.name } }}
{{ (fileAttribute ? 'add-edit-file-attribute.title.edit' : 'add-edit-file-attribute.title.new') | translate: { name: fileAttribute?.label } }}
</div>
<form (submit)="saveFileAttribute()" [formGroup]="fileAttributeForm">
<div class="dialog-content">
<div class="red-input-group required w-300">
<label translate="add-edit-file-attribute.form.name"></label>
<input formControlName="name" name="name" type="text" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" />
<input formControlName="label" name="label" type="text" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" />
</div>
<div class="red-input-group ignore-label-styles">

View File

@ -1,8 +1,9 @@
import { Component, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AppStateService } from '../../state/app-state.service';
import { FileAttribute, FileAttributesControllerService } from '@redaction/red-ui-http';
import { FileAttributeConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { computerize } from '../../utils/functions';
@Component({
selector: 'redaction-add-edit-file-attribute-dialog',
@ -11,7 +12,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
})
export class AddEditFileAttributeDialogComponent {
public fileAttributeForm: FormGroup;
public fileAttribute: FileAttribute;
public fileAttribute: FileAttributeConfig;
public ruleSetId: string;
constructor(
@ -19,13 +20,13 @@ export class AddEditFileAttributeDialogComponent {
private readonly _formBuilder: FormBuilder,
private readonly _fileAttributesService: FileAttributesControllerService,
public dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { fileAttribute: FileAttribute; ruleSetId: string }
@Inject(MAT_DIALOG_DATA) public data: { fileAttribute: FileAttributeConfig; ruleSetId: string }
) {
this.fileAttribute = data.fileAttribute;
this.ruleSetId = data.ruleSetId;
this.fileAttributeForm = this._formBuilder.group({
name: [this.fileAttribute?.name, Validators.required],
label: [this.fileAttribute?.label, Validators.required],
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
visible: [this.fileAttribute?.visible]
});
@ -48,12 +49,13 @@ export class AddEditFileAttributeDialogComponent {
}
async saveFileAttribute() {
const fileAttribute = {
const fileAttribute: FileAttributeConfig = {
id: this.fileAttribute?.id,
editable: !this.fileAttributeForm.get('readonly').value,
csvColumnHeader: this.fileAttribute?.csvColumnHeader || computerize(this.fileAttributeForm.get('label').value),
...this.fileAttributeForm.getRawValue()
};
await this._fileAttributesService.setFileAttributesConfiguration({ fileAttributes: [fileAttribute] }, this.ruleSetId).toPromise();
await this._fileAttributesService.setFileAttributesConfiguration(fileAttribute, this.ruleSetId).toPromise();
this.dialogRef.close(fileAttribute);
}
}

View File

@ -1,6 +1,6 @@
<section class="dialog">
<div class="dialog-header heading-l">
{{ 'confirm-delete-file-attribute.title' | translate: { name: fileAttribute.name } }}
{{ 'confirm-delete-file-attribute.title' | translate: { name: fileAttribute.label } }}
</div>
<div class="dialog-content">

View File

@ -1,6 +1,6 @@
import { Component, Inject } from '@angular/core';
import { AppStateService } from '../../state/app-state.service';
import { FileAttribute, FileAttributesControllerService } from '@redaction/red-ui-http';
import { FileAttributeConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@Component({
@ -9,7 +9,7 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
styleUrls: ['./confirm-delete-file-attribute-dialog.component.scss']
})
export class ConfirmDeleteFileAttributeDialogComponent {
public fileAttribute: FileAttribute;
public fileAttribute: FileAttributeConfig;
public ruleSetId: string;
public checkboxes = [{ value: false }, { value: false }];
@ -17,7 +17,7 @@ export class ConfirmDeleteFileAttributeDialogComponent {
private readonly _appStateService: AppStateService,
private readonly _fileAttributesService: FileAttributesControllerService,
public dialogRef: MatDialogRef<ConfirmDeleteFileAttributeDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { fileAttribute: FileAttribute; ruleSetId: string }
@Inject(MAT_DIALOG_DATA) public data: { fileAttribute: FileAttributeConfig; ruleSetId: string }
) {
this.fileAttribute = data.fileAttribute;
this.ruleSetId = data.ruleSetId;

View File

@ -3,7 +3,7 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import {
Colors,
DictionaryControllerService,
FileAttribute,
FileAttributeConfig,
FileManagementControllerService,
FileStatus,
ManualRedactionControllerService,
@ -30,6 +30,7 @@ import { RemoveAnnotationsDialogComponent } from './remove-annotations-dialog/re
import { ForceRedactionDialogComponent } from './force-redaction-dialog/force-redaction-dialog.component';
import { AddEditFileAttributeDialogComponent } from './add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
import { ConfirmDeleteFileAttributeDialogComponent } from './confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
import { DocumentInfoDialogComponent } from './document-info-dialog/document-info-dialog.component';
const dialogConfig = {
width: '662px',
@ -339,7 +340,11 @@ export class DialogService {
return ref;
}
public openAddEditFileAttributeDialog(fileAttribute: FileAttribute, ruleSetId: string, cb?: Function): MatDialogRef<AddEditFileAttributeDialogComponent> {
public openAddEditFileAttributeDialog(
fileAttribute: FileAttributeConfig,
ruleSetId: string,
cb?: Function
): MatDialogRef<AddEditFileAttributeDialogComponent> {
const ref = this._dialog.open(AddEditFileAttributeDialogComponent, {
...dialogConfig,
data: { fileAttribute, ruleSetId },
@ -356,7 +361,7 @@ export class DialogService {
}
public openConfirmDeleteFileAttributeDialog(
fileAttribute: FileAttribute,
fileAttribute: FileAttributeConfig,
ruleSetId: string,
cb?: Function
): MatDialogRef<ConfirmDeleteFileAttributeDialogComponent> {
@ -375,6 +380,22 @@ export class DialogService {
return ref;
}
public openDocumentInfoDialog(file: FileStatus, cb?: Function): MatDialogRef<DocumentInfoDialogComponent> {
const ref = this._dialog.open(DocumentInfoDialogComponent, {
...dialogConfig,
data: file,
autoFocus: true
});
ref.afterClosed().subscribe((result) => {
if (result && cb) {
cb(result);
}
});
return ref;
}
openRemoveAnnotationModal($event: MouseEvent, annotation: AnnotationWrapper, callback: () => void) {
$event?.stopPropagation();

View File

@ -0,0 +1,24 @@
<section class="dialog">
<div class="dialog-header heading-l" translate="document-info.title"></div>
<form (submit)="saveDocumentInfo()" [formGroup]="documentInfoForm">
<div class="dialog-content">
{{ file.filename }}
<!-- <div class="red-input-group required w-300">-->
<!-- <label translate="add-edit-file-attribute.form.name"></label>-->
<!-- <input formControlName="name" name="name" type="text" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" />-->
<!-- </div>-->
<!-- <div class="red-input-group ignore-label-styles">-->
<!-- <mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>-->
<!-- </div>-->
</div>
<div class="dialog-actions">
<button [disabled]="documentInfoForm.invalid" color="primary" mat-flat-button type="submit">
{{ 'document-info.save' | translate }}
</button>
</div>
</form>
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
</section>

View File

@ -0,0 +1,36 @@
import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FileAttributesControllerService, FileStatus } from '@redaction/red-ui-http';
import { AppStateService } from '../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'redaction-document-info-dialog',
templateUrl: './document-info-dialog.component.html',
styleUrls: ['./document-info-dialog.component.scss']
})
export class DocumentInfoDialogComponent implements OnInit {
public documentInfoForm: FormGroup;
public file: FileStatus;
constructor(
private readonly _appStateService: AppStateService,
private readonly _formBuilder: FormBuilder,
private readonly _fileAttributesService: FileAttributesControllerService,
public dialogRef: MatDialogRef<DocumentInfoDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: FileStatus
) {
this.file = this.data;
console.log(this.data);
this.documentInfoForm = this._formBuilder.group({
// name: [this.fileAttribute?.name, Validators.required],
// readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
// visible: [this.fileAttribute?.visible]
});
}
ngOnInit(): void {}
public saveDocumentInfo() {}
}

View File

@ -64,8 +64,9 @@ export class IconsModule {
'sort-asc',
'sort-desc',
'status',
'status-expand',
'status-collapse',
'status-expand',
'status-info',
'template',
'thumb-down',
'thumb-up',

View File

@ -50,7 +50,7 @@
</div>
</div>
<div class="table-header" redactionSyncWidth="table-item">
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
<div class="select-oval-placeholder"></div>
<redaction-table-col-name
@ -81,7 +81,7 @@
</div>
<div>
{{ attribute.name }}
{{ attribute.label }}
</div>
<!-- TODO-->
<!-- <div class="center">-->

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { PermissionsService } from '../../../utils/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FileAttribute, FileAttributesControllerService } from '@redaction/red-ui-http';
import { FileAttributeConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '../../../state/app-state.service';
import { ActivatedRoute } from '@angular/router';
import { debounce } from '../../../utils/debounce';
@ -15,8 +15,8 @@ import { DialogService } from '../../../dialogs/dialog.service';
})
export class FileAttributesListingScreenComponent implements OnInit {
public searchForm: FormGroup;
public attributes: FileAttribute[] = [];
public displayedAttributes: FileAttribute[] = [];
public attributes: FileAttributeConfig[] = [];
public displayedAttributes: FileAttributeConfig[] = [];
public selectedFileAttributeIds: string[] = [];
public viewReady = false;
@ -47,6 +47,7 @@ export class FileAttributesListingScreenComponent implements OnInit {
try {
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
this.attributes = response?.fileAttributes || [];
} catch (e) {
} finally {
this.displayedAttributes = [...this.attributes];
this._executeSearch();
@ -71,24 +72,24 @@ export class FileAttributesListingScreenComponent implements OnInit {
if (!value) {
value = { query: this.searchForm.get('query').value };
}
this.displayedAttributes = this.attributes.filter((attribute) => attribute.name.toLowerCase().includes(value.query.toLowerCase()));
this.displayedAttributes = this.attributes.filter((attribute) => attribute.label.toLowerCase().includes(value.query.toLowerCase()));
}
public openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttribute) {
public openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
$event.stopPropagation();
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
await this._loadData();
});
}
public openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttribute) {
public openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
$event.stopPropagation();
this._dialogService.openConfirmDeleteFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
await this._loadData();
});
}
public toggleAttributeSelected($event: MouseEvent, attribute: FileAttribute) {
public toggleAttributeSelected($event: MouseEvent, attribute: FileAttributeConfig) {
$event.stopPropagation();
const idx = this.selectedFileAttributeIds.indexOf(attribute.id);
if (idx === -1) {
@ -114,7 +115,7 @@ export class FileAttributesListingScreenComponent implements OnInit {
return this.selectedFileAttributeIds.length > 0;
}
public isAttributeSelected(attribute: FileAttribute) {
public isAttributeSelected(attribute: FileAttributeConfig) {
return this.selectedFileAttributeIds.indexOf(attribute.id) !== -1;
}
}

View File

@ -6,7 +6,7 @@ import { PermissionsService } from '../../../utils/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../utils/debounce';
import { RuleSetModel } from '@redaction/red-ui-http';
import { UserPreferenceService } from '../../../common/service/user-preference.service';
import { UserPreferenceService } from '../../../utils/user-preference.service';
@Component({
selector: 'redaction-rule-sets-listing-screen',

View File

@ -129,7 +129,11 @@
</ng-container>
<div class="vertical-line"></div>
<redaction-file-actions (actionPerformed)="fileActionPerformed($event)" *ngIf="viewReady"></redaction-file-actions>
<redaction-file-actions
(actionPerformed)="fileActionPerformed($event)"
[activeDocumentInfo]="viewDocumentInfo"
*ngIf="viewReady"
></redaction-file-actions>
<redaction-circle-button
(action)="toggleFullScreen()"
@ -199,119 +203,124 @@
></redaction-pdf-viewer>
</div>
<div class="right-container">
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
<div>
<redaction-filter
(filtersChanged)="filtersChanged($event)"
[chevron]="true"
[filterTemplate]="annotationFilterTemplate"
[actionsTemplate]="annotationFilterActionTemplate"
[filters]="annotationFilters"
></redaction-filter>
</div>
</div>
<div class="right-content">
<div
#quickNavigation
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="pagesPanelActive"
class="quick-navigation"
tabindex="0"
>
<div
class="jump"
[class.disabled]="!quickScrollFirstEnabled"
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
matTooltipPosition="above"
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
>
<mat-icon svgIcon="red:nav-first"></mat-icon>
</div>
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of displayedPages"
[active]="pageNumber === activeViewerPage"
[number]="pageNumber"
[viewedPages]="fileData.viewedPages"
>
</redaction-page-indicator>
</div>
<div
class="jump"
[class.disabled]="!quickScrollLastEnabled"
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
matTooltipPosition="above"
(click)="scrollQuickNavLast()"
>
<mat-icon svgIcon="red:nav-last"></mat-icon>
<div class="right-container" redactionHasScrollbar>
<redaction-document-info *ngIf="viewDocumentInfo" [file]="fileData.fileStatus" (closeDocumentInfoView)="viewDocumentInfo = false">
</redaction-document-info>
<ng-container *ngIf="!viewDocumentInfo">
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
<div>
<redaction-filter
(filtersChanged)="filtersChanged($event)"
[chevron]="true"
[filterTemplate]="annotationFilterTemplate"
[actionsTemplate]="annotationFilterActionTemplate"
[filters]="annotationFilters"
></redaction-filter>
</div>
</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 }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
></span>
</div>
<div class="right-content">
<div
#annotationsElement
#quickNavigation
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
redactionHasScrollbar
class="annotations"
tabindex="1"
[class.active-panel]="pagesPanelActive"
class="quick-navigation"
tabindex="0"
>
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
{{ 'file-preview.no-annotations-for-page' | translate }}
<div
class="jump"
[class.disabled]="!quickScrollFirstEnabled"
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
matTooltipPosition="above"
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
>
<mat-icon svgIcon="red:nav-first"></mat-icon>
</div>
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of displayedPages"
[active]="pageNumber === activeViewerPage"
[number]="pageNumber"
[viewedPages]="fileData.viewedPages"
>
</redaction-page-indicator>
</div>
<div
class="jump"
[class.disabled]="!quickScrollLastEnabled"
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
matTooltipPosition="above"
(click)="scrollQuickNavLast()"
>
<mat-icon svgIcon="red:nav-last"></mat-icon>
</div>
</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 }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
></span>
</div>
<div
(click)="annotationClicked(annotation)"
class="annotation-wrapper"
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
attr.annotation-id="{{ annotation.id }}"
attr.annotation-page="{{ activeViewerPage }}"
[class.active]="annotationIsSelected(annotation)"
#annotationsElement
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
redactionHasScrollbar
class="annotations"
tabindex="1"
>
<div class="active-marker"></div>
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
<div class="details">
<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 && annotation.dictionary !== 'manual'">
<strong
><span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.dictionary | humanize: false }}
</div>
<div *ngIf="annotation.content && !annotation.isHint">
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
{{ 'file-preview.no-annotations-for-page' | translate }}
</div>
<div
(click)="annotationClicked(annotation)"
class="annotation-wrapper"
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
attr.annotation-id="{{ annotation.id }}"
attr.annotation-page="{{ activeViewerPage }}"
[class.active]="annotationIsSelected(annotation)"
>
<div class="active-marker"></div>
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
<div class="details">
<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 && annotation.dictionary !== 'manual'">
<strong
><span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.dictionary | humanize: false }}
</div>
<div *ngIf="annotation.content && !annotation.isHint">
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
</div>
</div>
<redaction-annotation-actions
(annotationsChanged)="annotationsChangedByReviewAction($event)"
[annotation]="annotation"
[canPerformAnnotationActions]="canPerformAnnotationActions"
[viewer]="activeViewer"
></redaction-annotation-actions>
</div>
<redaction-annotation-actions
(annotationsChanged)="annotationsChangedByReviewAction($event)"
[annotation]="annotation"
[canPerformAnnotationActions]="canPerformAnnotationActions"
[viewer]="activeViewer"
></redaction-annotation-actions>
</div>
</redaction-hidden-action>
<redaction-comments [annotation]="annotation"></redaction-comments>
</redaction-hidden-action>
<redaction-comments [annotation]="annotation"></redaction-comments>
</div>
</div>
</div>
</div>
</div>
</div>
</ng-container>
</div>
</div>
</section>

View File

@ -30,27 +30,20 @@
width: 350px;
min-width: 350px;
.right-title {
&.has-scrollbar:hover {
::ng-deep redaction-document-info .right-title,
::ng-deep redaction-document-info .section {
padding-right: 13px;
}
}
::ng-deep.right-title {
height: 70px;
display: flex;
border-bottom: 1px solid $separator;
align-items: center;
justify-content: space-between;
padding: 0 24px;
.close-icon {
height: 14px;
width: 14px;
cursor: pointer;
}
> div {
display: flex;
redaction-circle-button {
margin-left: 2px;
}
}
}
.right-content {

View File

@ -146,6 +146,22 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
hideSkipped = false;
private _viewDocumentInfo = false;
public get viewDocumentInfo(): boolean {
return this._viewDocumentInfo;
}
public set viewDocumentInfo(value: boolean) {
this._viewDocumentInfo = value;
if (!value) {
setTimeout(() => {
this._scrollQuickNavigation();
this.scrollToSelectedAnnotation();
}, 0);
}
}
updateViewMode() {
const allAnnotations = this._instance.annotManager.getAnnotationsList();
@ -329,13 +345,16 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
@debounce()
private _scrollViews() {
if (this.viewDocumentInfo) {
return;
}
this._scrollQuickNavigation();
this._scrollAnnotations();
}
@debounce()
private scrollToSelectedAnnotation() {
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
if (this.viewDocumentInfo || !this.selectedAnnotations || this.selectedAnnotations.length === 0) {
return;
}
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[annotation-id="${this.firstSelectedAnnotation.id}"].active`);
@ -602,6 +621,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
public computeQuickNavButtonsState() {
if (this.viewDocumentInfo) {
return;
}
const element: HTMLElement = this._quickNavigationElement.nativeElement.querySelector(`#pages`);
const { scrollTop, scrollHeight, clientHeight } = element;
this.quickScrollFirstEnabled = scrollTop !== 0;
@ -670,6 +692,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this._updateCanPerformActions();
await this.appStateService.reloadActiveProjectFiles();
return;
case 'view-document-info':
this.viewDocumentInfo = !this.viewDocumentInfo;
return;
default:
this._updateCanPerformActions();
}

View File

@ -291,6 +291,18 @@
"view-toggle": "Redacted View",
"tabs": {
"quick-navigation": "Quick Navigation",
"document-info": {
"label": "Document Info",
"close": "Close Document Info",
"edit": "Edit Document Info",
"details": {
"project": "in {{projectName}}",
"pages": "{{pages}} pages",
"revised-pages": "{{pages}} revised pages",
"created-on": "Created on: {{date}}",
"due": "Due: {{date}}"
}
},
"annotations": {
"label": "Workload"
}
@ -303,6 +315,7 @@
"assign-me": "Assign to me",
"last-reviewer": "Last Reviewed by:",
"fullscreen": "Full Screen (F)",
"document-info": "Your Document Info lives here. This includes metadata required on each document.",
"new-tab-ssr": "Open Document in Server Side Rendering Mode",
"html-debug": "Open Document HTML Debug",
"download-original-file": "Download Original File",
@ -732,6 +745,11 @@
"checkbox-1": "All documents it is used on will be impacted",
"checkbox-2": "All inputted details on the documents will be lost"
},
"document-info": {
"title": "Introduce Document Info",
"save": "Save Document Info",
"save-approval": "Save and Send for Approval"
},
"user-listing": {
"table-header": {
"title": "{{length}} users"

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>F8E3057D-BE44-469F-9E28-9A04A0B36DF0</title>
<defs>
<rect id="path-1" x="0" y="61" width="1440" height="50"></rect>
<filter x="-0.5%" y="-10.0%" width="101.0%" height="128.0%" filterUnits="objectBoundingBox" id="filter-2">
<feOffset dx="0" dy="2" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="2" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0.88627451 0 0 0 0 0.894117647 0 0 0 0 0.91372549 0 0 0 1 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
</defs>
<g id="File-attributes" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Reviewer---Doc-Info-sidebar" transform="translate(-1256.000000, -79.000000)">
<rect x="0" y="0" width="1440" height="750"></rect>
<g id="Header-Document">
<g id="Group-6" transform="translate(1246.000000, 69.000000)">
<rect id="Rectangle" opacity="0.1" x="0" y="0" width="34" height="34" rx="17"></rect>
<g id="status" transform="translate(10.000000, 10.000000)" fill="currentColor">
<path d="M2.8,0 L0,0 L0,14 L14,14 L14,0 M12.6,1.4 L12.6,12.6 L1.4,12.6 L1.4,1.4 L2.8,1.4" id="pages" fill-rule="nonzero"></path>
<rect id="Rectangle" x="6.3" y="5.46" width="1.4" height="5.46"></rect>
<rect id="Rectangle" x="6.3" y="3.08" width="1.4" height="1.4"></rect>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -74,7 +74,7 @@
mat-icon {
width: 10px;
margin-right: 4px;
margin-right: 6px;
}
&:not(:last-child) {