RED-8748 - extracted file preview header logic in another component
This commit is contained in:
parent
690ffea6c5
commit
46e016d55e
@ -3,8 +3,6 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
||||
import { Roles } from '@users/roles';
|
||||
import { ImageCategory } from '../../utils/constants';
|
||||
import { ManualRedactionTypes } from '@red/domain';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-card',
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
<div class="page-header">
|
||||
<div class="flex flex-1">
|
||||
<redaction-view-switch></redaction-view-switch>
|
||||
</div>
|
||||
|
||||
<!-- TODO: mode this file preview header to a separate component-->
|
||||
<div #actionsWrapper class="flex-2 actions-container">
|
||||
<redaction-processing-indicator [file]="file" class="mr-16"></redaction-processing-indicator>
|
||||
|
||||
<redaction-user-management></redaction-user-management>
|
||||
|
||||
<ng-container *ngIf="permissionsService.isApprover(state.dossier()) && !!file.lastReviewer">
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<div class="all-caps-label mr-16 ml-8 label">
|
||||
{{ 'file-preview.last-assignee' | translate }}
|
||||
</div>
|
||||
|
||||
<iqser-initials-avatar [user]="lastAssignee()" [withName]="true"></iqser-initials-avatar>
|
||||
</ng-container>
|
||||
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<!-- TODO: mode these actions to a separate component -->
|
||||
<iqser-circle-button
|
||||
(action)="openComponentLogView()"
|
||||
*allow="roles.getRss"
|
||||
[attr.help-mode-key]="'editor_scm'"
|
||||
[tooltip]="'file-preview.open-rss-view' | translate"
|
||||
class="ml-8"
|
||||
icon="red:extract"
|
||||
tooltipPosition="below"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<redaction-file-actions
|
||||
[dossier]="state.dossier()"
|
||||
[file]="file"
|
||||
[helpModeKeyPrefix]="'editor'"
|
||||
[minWidth]="width"
|
||||
type="file-preview"
|
||||
iqserDisableStopPropagation
|
||||
></redaction-file-actions>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="getTables()"
|
||||
*allow="roles.getTables"
|
||||
[icon]="'red:csv'"
|
||||
[tooltip]="'file-preview.get-tables' | translate"
|
||||
class="ml-2"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleFullScreen()"
|
||||
[attr.help-mode-key]="'editor_full_screen'"
|
||||
[icon]="fullScreen ? 'red:exit-fullscreen' : 'red:fullscreen'"
|
||||
[tooltip]="'file-preview.fullscreen' | translate"
|
||||
class="ml-2"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- Dev Mode Features-->
|
||||
<iqser-circle-button
|
||||
(action)="downloadOriginalFile(file)"
|
||||
*ngIf="isIqserDevMode"
|
||||
[tooltip]="'file-preview.download-original-file' | translate"
|
||||
[type]="circleButtonTypes.primary"
|
||||
class="ml-8"
|
||||
icon="iqser:download"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- End Dev Mode Features-->
|
||||
|
||||
<iqser-circle-button
|
||||
*ngIf="!fullScreen"
|
||||
[attr.help-mode-key]="'editor_close'"
|
||||
[routerLink]="state.dossier().routerLink"
|
||||
[tooltip]="'common.close' | translate"
|
||||
class="ml-8"
|
||||
icon="iqser:close"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,9 @@
|
||||
.page-header {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
@ -0,0 +1,217 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
Input,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Roles } from '@users/roles';
|
||||
import { CircleButtonTypes, HelpModeService, IqserDialog, IqserPermissionsService, isIqserDevMode, LoadingService } from '@iqser/common-ui';
|
||||
import { Bind, Debounce, OnDetach } from '@iqser/common-ui/lib/utils';
|
||||
import { File } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import JSZip from 'jszip';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||
import { AnnotationDrawService } from '../../../pdf-viewer/services/annotation-draw.service';
|
||||
import { TablesService } from '../../services/tables.service';
|
||||
import { ALL_HOTKEYS } from '../../utils/constants';
|
||||
import { Router } from '@angular/router';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { download } from '@utils/file-download-utils';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { FileManagementService } from '@services/files/file-management.service';
|
||||
import { StructuredComponentManagementDialogComponent } from '../../dialogs/docu-mine/structured-component-management-dialog/structured-component-management-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-header',
|
||||
templateUrl: './file-header.component.html',
|
||||
styleUrls: ['/file-header.component.scss'],
|
||||
})
|
||||
export class FileHeaderComponent implements OnInit, AfterViewInit, OnDetach, OnDestroy {
|
||||
@ViewChild('actionsWrapper', { static: false }) private readonly _actionsWrapper: ElementRef;
|
||||
@Input() file: File;
|
||||
|
||||
protected readonly roles = Roles;
|
||||
protected readonly circleButtonTypes = CircleButtonTypes;
|
||||
|
||||
readonly lastAssignee = computed(() => this.getLastAssignee());
|
||||
readonly isIqserDevMode = isIqserDevMode();
|
||||
width: number;
|
||||
fullScreen = false;
|
||||
|
||||
constructor(
|
||||
private readonly _changeRef: ChangeDetectorRef,
|
||||
private readonly _iqserPermissionsService: IqserPermissionsService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _tablesService: TablesService,
|
||||
private readonly _router: Router,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _helpModeService: HelpModeService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
readonly state: FilePreviewStateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.#openComponentLogDialogIfDefault();
|
||||
document.documentElement.addEventListener('fullscreenchange', this.fullscreenListener);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const _observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||
this._updateItemWidth(entries[0]);
|
||||
});
|
||||
_observer.observe(this._actionsWrapper.nativeElement);
|
||||
}
|
||||
|
||||
ngOnDetach() {
|
||||
document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener);
|
||||
}
|
||||
|
||||
async downloadOriginalFile({ cacheIdentifier, dossierId, fileId, filename }: File) {
|
||||
const originalFile = this._fileManagementService.downloadOriginal(dossierId, fileId, 'response', cacheIdentifier);
|
||||
download(await firstValueFrom(originalFile), filename);
|
||||
}
|
||||
|
||||
getLastAssignee() {
|
||||
const { isApproved, lastReviewer, lastApprover } = this.state.file();
|
||||
const isRss = this._iqserPermissionsService.has(this.roles.getRss);
|
||||
return isApproved ? (isRss ? lastReviewer : lastApprover) : lastReviewer;
|
||||
}
|
||||
|
||||
openComponentLogView() {
|
||||
const data = { file: this.state.file(), dictionaries: this.state.dictionaries };
|
||||
this._iqserDialog.openDefault(StructuredComponentManagementDialogComponent, { data });
|
||||
}
|
||||
|
||||
async getTables() {
|
||||
this._loadingService.start();
|
||||
|
||||
const currentPage = this._pdf.currentPage();
|
||||
const tables = await this._tablesService.get(this.state.dossierId, this.state.fileId, this._pdf.currentPage());
|
||||
await this._annotationDrawService.drawTables(tables, currentPage, this.state.dossierTemplateId);
|
||||
|
||||
const filename = this.state.file().filename;
|
||||
const zip = new JSZip();
|
||||
|
||||
tables.forEach((t, index) => {
|
||||
const blob = new Blob([atob(t.csvAsBytes)], {
|
||||
type: 'text/csv;charset=utf-8',
|
||||
});
|
||||
zip.file(filename + '_page' + currentPage + '_table' + (index + 1) + '.csv', blob);
|
||||
});
|
||||
|
||||
saveAs(await zip.generateAsync({ type: 'blob' }), filename + '_tables.zip');
|
||||
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
this.fullScreen = !this.fullScreen;
|
||||
if (this.fullScreen) {
|
||||
this.#openFullScreen();
|
||||
} else {
|
||||
this.closeFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
closeFullScreen() {
|
||||
if (!!document.fullscreenElement && document.exitFullscreen) {
|
||||
document.exitFullscreen().then();
|
||||
}
|
||||
}
|
||||
|
||||
@Bind()
|
||||
fullscreenListener() {
|
||||
if (!document.fullscreenElement) {
|
||||
this.fullScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent) {
|
||||
if (this._router.url.indexOf('/file/') < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ALL_HOTKEYS.includes($event.key) || this._dialog.openDialogs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (['Escape'].includes($event.key)) {
|
||||
$event.preventDefault();
|
||||
if (this._annotationManager.resizingAnnotationId) {
|
||||
const resizedAnnotation = this._fileDataService
|
||||
.annotations()
|
||||
.find(annotation => annotation.id === this._annotationManager.resizingAnnotationId);
|
||||
this._annotationActionsService.cancelResize(resizedAnnotation).then();
|
||||
}
|
||||
this.fullScreen = false;
|
||||
this.closeFullScreen();
|
||||
this._changeRef.markForCheck();
|
||||
}
|
||||
|
||||
if (!$event.ctrlKey && !$event.metaKey && ['f', 'F'].includes($event.key)) {
|
||||
// if you type in an input, don't toggle full-screen
|
||||
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
|
||||
return;
|
||||
}
|
||||
this.toggleFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
if (['h', 'H'].includes($event.key)) {
|
||||
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
|
||||
return;
|
||||
}
|
||||
this._ngZone.run(() => {
|
||||
window.focus();
|
||||
this._helpModeService.activateHelpMode(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#openFullScreen() {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement.requestFullscreen) {
|
||||
documentElement.requestFullscreen().then();
|
||||
}
|
||||
}
|
||||
|
||||
#openComponentLogDialogIfDefault() {
|
||||
if (this.permissionsService.canViewRssDialog() && this._userPreferenceService.getOpenScmDialogByDefault()) {
|
||||
this.openComponentLogView();
|
||||
}
|
||||
}
|
||||
|
||||
@Debounce(30)
|
||||
private _updateItemWidth(entry: ResizeObserverEntry): void {
|
||||
this.width = entry.contentRect.width;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
}
|
||||
@ -1,21 +1,30 @@
|
||||
import { KeyValuePipe, NgForOf, NgIf } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component, Inject, OnInit, signal } from '@angular/core';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
|
||||
import { BaseDialogComponent, CircleButtonComponent, EditableInputComponent, IconButtonComponent } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ComponentLogEntry, Dictionary, IFile, WorkflowFileStatuses } from '@red/domain';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { ChangeDetectionStrategy, Component, Inject, OnInit, signal } from '@angular/core';
|
||||
import { KeyValuePipe, NgForOf, NgIf } from '@angular/common';
|
||||
import {
|
||||
CircleButtonComponent,
|
||||
EditableInputComponent,
|
||||
IconButtonComponent,
|
||||
IconButtonTypes,
|
||||
IqserDialogComponent,
|
||||
LoadingService,
|
||||
} from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
||||
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
|
||||
import { ComponentLogService } from '@services/files/component-log.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
interface ScmData {
|
||||
file: IFile;
|
||||
dictionaries: Dictionary[];
|
||||
}
|
||||
|
||||
interface ScmResult {}
|
||||
|
||||
@Component({
|
||||
templateUrl: './structured-component-management-dialog.component.html',
|
||||
styleUrls: ['./structured-component-management-dialog.component.scss'],
|
||||
@ -34,18 +43,22 @@ interface ScmData {
|
||||
ReplaceNbspPipe,
|
||||
],
|
||||
})
|
||||
export class StructuredComponentManagementDialogComponent extends BaseDialogComponent implements OnInit {
|
||||
export class StructuredComponentManagementDialogComponent
|
||||
extends IqserDialogComponent<StructuredComponentManagementDialogComponent, ScmData, ScmResult>
|
||||
implements OnInit
|
||||
{
|
||||
readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
||||
readonly openScmDialogByDefault = signal(this.userPreferences.getOpenScmDialogByDefault());
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
|
||||
constructor(
|
||||
protected readonly _dialogRef: MatDialogRef<StructuredComponentManagementDialogComponent>,
|
||||
private readonly _componentLogService: ComponentLogService,
|
||||
readonly userPreferences: UserPreferenceService,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
readonly userPreferences: UserPreferenceService,
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: ScmData,
|
||||
) {
|
||||
super(_dialogRef);
|
||||
super();
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
@ -60,8 +73,6 @@ export class StructuredComponentManagementDialogComponent extends BaseDialogComp
|
||||
return `value-cell-${index}`;
|
||||
}
|
||||
|
||||
originalOrder = (): number => 0;
|
||||
|
||||
exportJSON() {
|
||||
return firstValueFrom(
|
||||
this._componentLogService.exportJSON(this.data.file.dossierTemplateId, this.data.file.dossierId, this.data.file),
|
||||
@ -1,90 +1,5 @@
|
||||
<section *ngIf="state.file() as file">
|
||||
<div class="page-header">
|
||||
<div class="flex flex-1">
|
||||
<redaction-view-switch></redaction-view-switch>
|
||||
</div>
|
||||
|
||||
<!-- TODO: mode this file preview header to a separate component-->
|
||||
<div #actionsWrapper class="flex-2 actions-container">
|
||||
<redaction-processing-indicator [file]="file" class="mr-16"></redaction-processing-indicator>
|
||||
|
||||
<redaction-user-management></redaction-user-management>
|
||||
|
||||
<ng-container *ngIf="permissionsService.isApprover(state.dossier()) && !!file.lastReviewer">
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<div class="all-caps-label mr-16 ml-8 label">
|
||||
{{ 'file-preview.last-assignee' | translate }}
|
||||
</div>
|
||||
|
||||
<iqser-initials-avatar [user]="lastAssignee()" [withName]="true"></iqser-initials-avatar>
|
||||
</ng-container>
|
||||
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<!-- TODO: mode these actions to a separate component -->
|
||||
<iqser-circle-button
|
||||
(action)="openComponentLogView()"
|
||||
*allow="roles.getRss"
|
||||
[attr.help-mode-key]="'editor_scm'"
|
||||
[tooltip]="'file-preview.open-rss-view' | translate"
|
||||
class="ml-8"
|
||||
icon="red:extract"
|
||||
tooltipPosition="below"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<redaction-file-actions
|
||||
[dossier]="state.dossier()"
|
||||
[file]="file"
|
||||
[helpModeKeyPrefix]="'editor'"
|
||||
[minWidth]="width"
|
||||
type="file-preview"
|
||||
iqserDisableStopPropagation
|
||||
></redaction-file-actions>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="getTables()"
|
||||
*allow="roles.getTables"
|
||||
[icon]="'red:csv'"
|
||||
[tooltip]="'file-preview.get-tables' | translate"
|
||||
class="ml-2"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleFullScreen()"
|
||||
[attr.help-mode-key]="'editor_full_screen'"
|
||||
[icon]="fullScreen ? 'red:exit-fullscreen' : 'red:fullscreen'"
|
||||
[tooltip]="'file-preview.fullscreen' | translate"
|
||||
class="ml-2"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- Dev Mode Features-->
|
||||
<iqser-circle-button
|
||||
(action)="downloadOriginalFile(file)"
|
||||
*ngIf="isIqserDevMode"
|
||||
[tooltip]="'file-preview.download-original-file' | translate"
|
||||
[type]="circleButtonTypes.primary"
|
||||
class="ml-8"
|
||||
icon="iqser:download"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- End Dev Mode Features-->
|
||||
|
||||
<iqser-circle-button
|
||||
*ngIf="!fullScreen"
|
||||
[attr.help-mode-key]="'editor_close'"
|
||||
[routerLink]="state.dossier().routerLink"
|
||||
[tooltip]="'common.close' | translate"
|
||||
class="ml-8"
|
||||
icon="iqser:close"
|
||||
iqserDisableStopPropagation
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-file-header [file]="file"></redaction-file-header>
|
||||
|
||||
<div class="overlay-shadow"></div>
|
||||
|
||||
@ -94,7 +9,9 @@
|
||||
</div>
|
||||
|
||||
<div class="right-container">
|
||||
<redaction-file-preview-right-container [iqserDisableStopPropagation]="state.isEditingReviewer()"></redaction-file-preview-right-container>
|
||||
<redaction-file-preview-right-container
|
||||
[iqserDisableStopPropagation]="state.isEditingReviewer()"
|
||||
></redaction-file-preview-right-container>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -5,16 +5,6 @@
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.actions-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content-inner {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@ -1,18 +1,4 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
computed,
|
||||
effect,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
||||
@ -23,16 +9,13 @@ import {
|
||||
CustomError,
|
||||
ErrorService,
|
||||
getConfig,
|
||||
HelpModeService,
|
||||
IConfirmationDialogData,
|
||||
IqserDialog,
|
||||
IqserPermissionsService,
|
||||
isIqserDevMode,
|
||||
LoadingService,
|
||||
Toaster,
|
||||
} from '@iqser/common-ui';
|
||||
import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
|
||||
import { AutoUnsubscribe, Bind, bool, Debounce, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
|
||||
import { AutoUnsubscribe, Bind, bool, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ManualRedactionEntryTypes, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { Dictionary, File, ViewModes } from '@red/domain';
|
||||
@ -46,8 +29,6 @@ import { PermissionsService } from '@services/permissions.service';
|
||||
import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||
import { saveAs } from 'file-saver';
|
||||
import JSZip from 'jszip';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { combineLatest, first, firstValueFrom, Observable, of, pairwise } from 'rxjs';
|
||||
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
@ -73,36 +54,25 @@ import { ManualRedactionService } from './services/manual-redaction.service';
|
||||
import { PdfProxyService } from './services/pdf-proxy.service';
|
||||
import { SkippedService } from './services/skipped.service';
|
||||
import { StampService } from './services/stamp.service';
|
||||
import { TablesService } from './services/tables.service';
|
||||
import { ViewModeService } from './services/view-mode.service';
|
||||
import { ALL_HOTKEYS } from './utils/constants';
|
||||
import { RedactTextData } from './utils/dialog-types';
|
||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||
|
||||
@Component({
|
||||
templateUrl: './file-preview-screen.component.html',
|
||||
styleUrls: ['./file-preview-screen.component.scss'],
|
||||
providers: filePreviewScreenProviders,
|
||||
})
|
||||
export class FilePreviewScreenComponent
|
||||
extends AutoUnsubscribe
|
||||
implements AfterViewInit, OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate
|
||||
{
|
||||
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
readonly roles = Roles;
|
||||
fullScreen = false;
|
||||
readonly fileId = this.state.fileId;
|
||||
readonly dossierId = this.state.dossierId;
|
||||
readonly lastAssignee = computed(() => this.getLastAssignee());
|
||||
width: number;
|
||||
readonly isIqserDevMode = isIqserDevMode();
|
||||
@ViewChild('annotationFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
static: false,
|
||||
})
|
||||
private readonly _filterTemplate: TemplateRef<unknown>;
|
||||
#loadAllAnnotationsEnabled = false;
|
||||
@ViewChild('actionsWrapper', { static: false }) private readonly _actionsWrapper: ElementRef;
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
constructor(
|
||||
@ -112,7 +82,6 @@ export class FilePreviewScreenComponent
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
readonly pdfProxyService: PdfProxyService,
|
||||
readonly configService: ConfigService,
|
||||
private readonly _iqserPermissionsService: IqserPermissionsService,
|
||||
private readonly _listingService: AnnotationsListingService,
|
||||
private readonly _router: Router,
|
||||
private readonly _ngZone: NgZone,
|
||||
@ -141,11 +110,7 @@ export class FilePreviewScreenComponent
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _readableRedactionsService: ReadableRedactionsService,
|
||||
private readonly _helpModeService: HelpModeService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _tablesService: TablesService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
) {
|
||||
super();
|
||||
effect(() => {
|
||||
@ -211,12 +176,6 @@ export class FilePreviewScreenComponent
|
||||
);
|
||||
}
|
||||
|
||||
getLastAssignee() {
|
||||
const { isApproved, lastReviewer, lastApprover } = this.state.file();
|
||||
const isRss = this._iqserPermissionsService.has(this.roles.getRss);
|
||||
return isApproved ? (isRss ? lastReviewer : lastApprover) : lastReviewer;
|
||||
}
|
||||
|
||||
deleteEarmarksOnViewChange$() {
|
||||
const isChangingFromEarmarksViewMode$ = this._viewModeService.viewMode$.pipe(
|
||||
pairwise(),
|
||||
@ -293,24 +252,15 @@ export class FilePreviewScreenComponent
|
||||
this._viewerHeaderService.resetCompareButtons();
|
||||
this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files)
|
||||
super.ngOnDetach();
|
||||
document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener);
|
||||
this.pdf.instance.UI.hotkeys.off('esc');
|
||||
this._changeRef.markForCheck();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
document.documentElement.removeEventListener('fullscreenchange', this.fullscreenListener);
|
||||
this.pdf.instance.UI.hotkeys.off('esc');
|
||||
super.ngOnDestroy();
|
||||
}
|
||||
|
||||
@Bind()
|
||||
fullscreenListener() {
|
||||
if (!document.fullscreenElement) {
|
||||
this.fullScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Bind()
|
||||
handleDeleteRectangleOnEsc($event: KeyboardEvent) {
|
||||
$event.preventDefault();
|
||||
@ -355,19 +305,10 @@ export class FilePreviewScreenComponent
|
||||
|
||||
this.pdfProxyService.configureElements();
|
||||
this.#restoreOldFilters();
|
||||
document.documentElement.addEventListener('fullscreenchange', this.fullscreenListener);
|
||||
this.pdf.instance.UI.hotkeys.on('esc', this.handleDeleteRectangleOnEsc);
|
||||
this.#openComponentLogDialogIfDefault();
|
||||
this._viewerHeaderService.resetLayers();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const _observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||
this._updateItemWidth(entries[0]);
|
||||
});
|
||||
_observer.observe(this._actionsWrapper.nativeElement);
|
||||
}
|
||||
|
||||
openManualAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
|
||||
const file = this.state.file();
|
||||
|
||||
@ -393,59 +334,6 @@ export class FilePreviewScreenComponent
|
||||
);
|
||||
}
|
||||
|
||||
toggleFullScreen() {
|
||||
this.fullScreen = !this.fullScreen;
|
||||
if (this.fullScreen) {
|
||||
this.#openFullScreen();
|
||||
} else {
|
||||
this.closeFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent) {
|
||||
if (this._router.url.indexOf('/file/') < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ALL_HOTKEYS.includes($event.key) || this._dialog.openDialogs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (['Escape'].includes($event.key)) {
|
||||
$event.preventDefault();
|
||||
if (this._annotationManager.resizingAnnotationId) {
|
||||
const resizedAnnotation = this._fileDataService
|
||||
.annotations()
|
||||
.find(annotation => annotation.id === this._annotationManager.resizingAnnotationId);
|
||||
this._annotationActionsService.cancelResize(resizedAnnotation).then();
|
||||
}
|
||||
this.fullScreen = false;
|
||||
this.closeFullScreen();
|
||||
this._changeRef.markForCheck();
|
||||
}
|
||||
|
||||
if (!$event.ctrlKey && !$event.metaKey && ['f', 'F'].includes($event.key)) {
|
||||
// if you type in an input, don't toggle full-screen
|
||||
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
|
||||
return;
|
||||
}
|
||||
this.toggleFullScreen();
|
||||
return;
|
||||
}
|
||||
|
||||
if (['h', 'H'].includes($event.key)) {
|
||||
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
|
||||
return;
|
||||
}
|
||||
this._ngZone.run(() => {
|
||||
window.focus();
|
||||
this._helpModeService.activateHelpMode(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async viewerReady(pageNumber?: string) {
|
||||
if (pageNumber) {
|
||||
const file = this.state.file();
|
||||
@ -467,21 +355,6 @@ export class FilePreviewScreenComponent
|
||||
this._changeRef.markForCheck();
|
||||
}
|
||||
|
||||
closeFullScreen() {
|
||||
if (!!document.fullscreenElement && document.exitFullscreen) {
|
||||
document.exitFullscreen().then();
|
||||
}
|
||||
}
|
||||
|
||||
async downloadOriginalFile({ cacheIdentifier, dossierId, fileId, filename }: File) {
|
||||
const originalFile = this._fileManagementService.downloadOriginal(dossierId, fileId, 'response', cacheIdentifier);
|
||||
download(await firstValueFrom(originalFile), filename);
|
||||
}
|
||||
|
||||
openComponentLogView() {
|
||||
this._dialogService.openDialog('componentLog', { file: this.state.file(), dictionaries: this.state.dictionaries });
|
||||
}
|
||||
|
||||
loadAnnotations$() {
|
||||
const annotations$ = this._fileDataService.annotations$.pipe(
|
||||
startWith([] as AnnotationWrapper[]),
|
||||
@ -527,28 +400,6 @@ export class FilePreviewScreenComponent
|
||||
return this.#cleanupAndRedrawAnnotations(annotationsToDraw);
|
||||
}
|
||||
|
||||
async getTables() {
|
||||
this._loadingService.start();
|
||||
|
||||
const currentPage = this.pdf.currentPage();
|
||||
const tables = await this._tablesService.get(this.state.dossierId, this.state.fileId, this.pdf.currentPage());
|
||||
await this._annotationDrawService.drawTables(tables, currentPage, this.state.dossierTemplateId);
|
||||
|
||||
const filename = this.state.file().filename;
|
||||
const zip = new JSZip();
|
||||
|
||||
tables.forEach((t, index) => {
|
||||
const blob = new Blob([atob(t.csvAsBytes)], {
|
||||
type: 'text/csv;charset=utf-8',
|
||||
});
|
||||
zip.file(filename + '_page' + currentPage + '_table' + (index + 1) + '.csv', blob);
|
||||
});
|
||||
|
||||
saveAs(await zip.generateAsync({ type: 'blob' }), filename + '_tables.zip');
|
||||
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
async #openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
|
||||
const file = this.state.file();
|
||||
|
||||
@ -572,12 +423,6 @@ export class FilePreviewScreenComponent
|
||||
return firstValueFrom(addAndReload$.pipe(catchError(() => of(undefined))));
|
||||
}
|
||||
|
||||
@Debounce(30)
|
||||
private _updateItemWidth(entry: ResizeObserverEntry): void {
|
||||
this.width = entry.contentRect.width;
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
#getAnnotationsToDraw(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||
const currentPage = this.pdf.currentPage();
|
||||
const currentPageAnnotations = this._annotationManager.get(a => a.getPageNumber() === currentPage);
|
||||
@ -711,10 +556,6 @@ export class FilePreviewScreenComponent
|
||||
.pipe(tap(() => this.#handleDeletedFile()))
|
||||
.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = this._documentViewer.keyUp$.subscribe($event => {
|
||||
this.handleKeyEvent($event);
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this.#earmarks$.subscribe();
|
||||
this.addActiveScreenSubscription = this.deleteEarmarksOnViewChange$().subscribe();
|
||||
|
||||
@ -845,13 +686,6 @@ export class FilePreviewScreenComponent
|
||||
});
|
||||
}
|
||||
|
||||
#openFullScreen() {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement.requestFullscreen) {
|
||||
documentElement.requestFullscreen().then();
|
||||
}
|
||||
}
|
||||
|
||||
#navigateToDossier() {
|
||||
this._logger.info('Navigating to ', this.state.dossier().dossierName);
|
||||
return this._router.navigate([this.state.dossier().routerLink]);
|
||||
@ -878,12 +712,6 @@ export class FilePreviewScreenComponent
|
||||
});
|
||||
}
|
||||
|
||||
#openComponentLogDialogIfDefault() {
|
||||
if (this.permissionsService.canViewRssDialog() && this.userPreferenceService.getOpenScmDialogByDefault()) {
|
||||
this.openComponentLogView();
|
||||
}
|
||||
}
|
||||
|
||||
#getRedactTextDialog(data: RedactTextData) {
|
||||
if (this.#isDocumine) {
|
||||
return this._iqserDialog.openDefault(AddAnnotationDialogComponent, { data });
|
||||
|
||||
@ -70,6 +70,7 @@ import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||
import { TablesService } from './services/tables.service';
|
||||
import { FileHeaderComponent } from './components/file-header/file-header.component';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
@ -119,6 +120,7 @@ const components = [
|
||||
FilePreviewScreenComponent,
|
||||
FilePreviewRightContainerComponent,
|
||||
ReadonlyBannerComponent,
|
||||
FileHeaderComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@ -6,16 +6,8 @@ import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/doc
|
||||
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
|
||||
import { HighlightActionDialogComponent } from '../dialogs/highlight-action-dialog/highlight-action-dialog.component';
|
||||
import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { StructuredComponentManagementDialogComponent } from '../dialogs/structured-component-management-dialog/structured-component-management-dialog.component';
|
||||
|
||||
type DialogType =
|
||||
| 'confirm'
|
||||
| 'documentInfo'
|
||||
| 'componentLog'
|
||||
| 'changeLegalBasis'
|
||||
| 'forceAnnotation'
|
||||
| 'manualAnnotation'
|
||||
| 'highlightAction';
|
||||
type DialogType = 'confirm' | 'documentInfo' | 'changeLegalBasis' | 'forceAnnotation' | 'manualAnnotation' | 'highlightAction';
|
||||
|
||||
@Injectable()
|
||||
export class FilePreviewDialogService extends DialogService<DialogType> {
|
||||
@ -41,10 +33,6 @@ export class FilePreviewDialogService extends DialogService<DialogType> {
|
||||
highlightAction: {
|
||||
component: HighlightActionDialogComponent,
|
||||
},
|
||||
componentLog: {
|
||||
component: StructuredComponentManagementDialogComponent,
|
||||
dialogConfig: { width: '90vw' },
|
||||
},
|
||||
};
|
||||
|
||||
constructor(protected readonly _dialog: MatDialog) {
|
||||
|
||||
@ -133,9 +133,11 @@ export class PdfProxyService {
|
||||
}
|
||||
|
||||
configureElements() {
|
||||
const hexColor = this._dictionariesMapService.get(this._state.dossierTemplateId, 'manual').hexColor;
|
||||
const color = this._annotationDrawService.convertColor(hexColor);
|
||||
this._documentViewer.setRectangleToolStyles(color);
|
||||
const hexColor = this._dictionariesMapService.get(this._state.dossierTemplateId, 'manual')?.hexColor;
|
||||
if (hexColor) {
|
||||
const color = this._annotationDrawService.convertColor(hexColor);
|
||||
this._documentViewer.setRectangleToolStyles(color);
|
||||
}
|
||||
}
|
||||
|
||||
#configureRectangleAnnotationPopup(annotation: Annotation) {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dan1.iqser.cloud",
|
||||
"API_URL": "https://frontend2.iqser.cloud",
|
||||
"APP_NAME": "RedactManager",
|
||||
"IS_DOCUMINE": false,
|
||||
"IS_DOCUMINE": true,
|
||||
"RULE_EDITOR_DEV_ONLY": false,
|
||||
"AUTO_READ_TIME": 3,
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
@ -13,13 +13,13 @@
|
||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"OAUTH_IDP_HINT": null,
|
||||
"OAUTH_URL": "https://dan1.iqser.cloud/auth",
|
||||
"OAUTH_URL": "https://frontend2.iqser.cloud/auth",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||
"ANNOTATIONS_THRESHOLD": 1000,
|
||||
"THEME": "redact",
|
||||
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/redact/",
|
||||
"THEME": "scm",
|
||||
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/scm/",
|
||||
"AVAILABLE_NOTIFICATIONS_DAYS": 30,
|
||||
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES": 60,
|
||||
"NOTIFICATIONS_THRESHOLD": 1000,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user