diff --git a/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen/reports-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen/reports-screen.component.ts index d0c835782..7cc2e0add 100644 --- a/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen/reports-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/reports/reports-screen/reports-screen.component.ts @@ -149,9 +149,8 @@ export class ReportsScreenComponent implements OnInit { } private async _loadReportTemplates() { - this.availableTemplates$.next( - await firstValueFrom(this._reportTemplateService.getAvailableReportTemplates(this.#dossierTemplateId)), - ); + const reportTemplates = await this._reportTemplateService.getAvailableReportTemplates(this.#dossierTemplateId); + this.availableTemplates$.next(reportTemplates); } private async _loadPlaceholders() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts index 29be6a15d..8b376701f 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts @@ -214,18 +214,19 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy return; } - if ($event.key === 'ArrowLeft') { + if (!$event.metaKey && !$event.ctrlKey && $event.key === 'ArrowLeft') { this.pagesPanelActive = true; this._changeDetectorRef.markForCheck(); return; } - if ($event.key === 'ArrowRight') { + if (!$event.metaKey && !$event.ctrlKey && $event.key === 'ArrowRight') { this.pagesPanelActive = false; // if we activated annotationsPanel - // select first annotation from this page in case there is no // selected annotation on this page and not in multi select mode if (!this.pagesPanelActive && !this.multiSelectService.isActive) { + this._documentViewer.clearSelection(); this._selectFirstAnnotationOnCurrentPageIfNecessary(); } this._changeDetectorRef.markForCheck(); diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.html index cd30afbae..7c22adccb 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.html @@ -1,57 +1,72 @@
-
+

-
+
Component
Value
Transformation
-
Annotations
+
+ Annotations +
+
+
Type
+
Rule
+
Pages
+
Reason
+
+
{{ entry.key }}
-
- - -
+ + -
-
+ +
{{ entry.value.transformation }}
-
{{ entry.value.scmAnnotations | json }}
+
+ +
{{ annotation.type }}
+
{{ annotation.ruleNumber }}
+
{{ annotation.pages.join(',') }}
+
{{ annotation.reason }}
+
+
- - -
- +
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.scss b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.scss index f741ead8d..60782ce67 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.scss +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.scss @@ -47,3 +47,8 @@ font-weight: 600; } } + +.annotation-grid { + display: grid; + grid-template-columns: 3fr 1fr 1fr 5fr; +} diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts index 603c1ddd5..62a8119ab 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/rss-dialog/rss-dialog.component.ts @@ -1,9 +1,9 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { BaseDialogComponent, IconButtonTypes } from '@iqser/common-ui'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { RssService } from '@services/files/rss.service'; import { IFile, IRssEntry } from '@red/domain'; -import { firstValueFrom, Observable } from 'rxjs'; +import { BehaviorSubject, firstValueFrom } from 'rxjs'; import { map } from 'rxjs/operators'; import { FilesMapService } from '@services/files/files-map.service'; import { UserPreferenceService } from '@users/user-preference.service'; @@ -17,12 +17,10 @@ interface RssData { templateUrl: './rss-dialog.component.html', styleUrls: ['./rss-dialog.component.scss'], }) -export class RssDialogComponent extends BaseDialogComponent { +export class RssDialogComponent extends BaseDialogComponent implements OnInit { readonly iconButtonTypes = IconButtonTypes; - rssData$: Observable; - - originalOrder = (a: KeyValue, b: KeyValue): number => 0; + rssData$ = new BehaviorSubject(null); constructor( protected readonly _dialogRef: MatDialogRef, @@ -32,22 +30,14 @@ export class RssDialogComponent extends BaseDialogComponent { @Inject(MAT_DIALOG_DATA) readonly data: RssData, ) { super(_dialogRef); - this.rssData$ = this._rssService.getRSSData(this.data.file.dossierId, this.data.file.fileId).pipe( - map(entry => { - const mapped = {}; - for (const key of Object.keys(entry.result)) { - const newKey = key.replace(new RegExp('_', 'g'), ' '); - (entry.result[key]).originalKey = key; - mapped[newKey] = entry.result[key]; - } - return { - filaName: entry.filaName, - result: mapped, - }; - }), - ); } + async ngOnInit(): Promise { + await this.#loadData(); + } + + originalOrder = (a: KeyValue, b: KeyValue): number => 0; + exportJSON() { this._rssService.exportJSON(this.data.file.dossierId, this.data.file.fileId, this.data.file.filename).subscribe(); } @@ -68,14 +58,39 @@ export class RssDialogComponent extends BaseDialogComponent { this.exportJSON(); } - undo() { - console.log('Undo'); + async undo(entry: KeyValue) { + this._loadingService.start(); + await firstValueFrom(this._rssService.revertOverride(this.data.file.dossierId, this.data.file.fileId, [entry.value.originalKey])); + await this.#loadData(); } - saveEdit(event: string, originalKey: string) { - console.log(event, originalKey); - /** - * https://qa2.iqser.cloud/redaction-gateway-v1/swagger-ui/index.html#/rss-controller/revertOverrides - */ + async saveEdit(event: string, entry: KeyValue) { + this._loadingService.start(); + await firstValueFrom( + this._rssService.override(this.data.file.dossierId, this.data.file.fileId, { [entry.value.originalKey]: event }), + ); + await this.#loadData(); + } + + async #loadData(): Promise { + this._loadingService.start(); + const rssData = await firstValueFrom( + this._rssService.getRSSData(this.data.file.dossierId, this.data.file.fileId).pipe( + map(entry => { + const mapped = {}; + for (const key of Object.keys(entry.result)) { + const newKey = key.replace(new RegExp('_', 'g'), ' '); + (entry.result[key]).originalKey = key; + mapped[newKey] = entry.result[key]; + } + return { + filaName: entry.filaName, + result: mapped, + }; + }), + ), + ); + this.rssData$.next(rssData); + this._loadingService.stop(); } } diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts index b24a59e90..cc93d8ec2 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts @@ -12,7 +12,6 @@ import { ViewChild, } from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router'; -import { Core } from '@pdftron/webviewer'; import { AutoUnsubscribe, bool, @@ -26,7 +25,6 @@ import { HelpModeService, List, LoadingService, - log, NestedFilter, OnAttach, OnDetach, @@ -73,7 +71,6 @@ import { ConfigService } from '@services/config.service'; import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service'; import { ROLES } from '@users/roles'; import { SuggestionsService } from './services/suggestions.service'; -import Annotation = Core.Annotations.Annotation; const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]; @@ -92,10 +89,7 @@ export class FilePreviewScreenComponent fullScreen = false; readonly fileId = this.state.fileId; readonly dossierId = this.state.dossierId; - readonly file$ = this.state.file$.pipe( - tap(file => this._fileDataService.loadAnnotations(file)), - log('file'), - ); + readonly file$ = this.state.file$.pipe(tap(file => this._fileDataService.loadAnnotations(file))); width: number; @ViewChild('annotationFilterTemplate', { read: TemplateRef, @@ -180,6 +174,7 @@ export class FilePreviewScreenComponent const earmarks$ = isEarmarksViewMode$.pipe( tap(() => this._loadingService.start()), switchMap(() => this._fileDataService.loadEarmarks()), + switchMap(() => this._fileDataService.earmarks$), tap(() => this.updateViewMode().then(() => this._loadingService.stop())), ); @@ -223,7 +218,7 @@ export class FilePreviewScreenComponent switch (this._viewModeService.viewMode) { case ViewModes.STANDARD: { - this._setAnnotationsColor(redactions, 'annotationColor'); + this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor'); const wrappers = await this._fileDataService.annotations; const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id); const standardEntries = annotations @@ -232,7 +227,7 @@ export class FilePreviewScreenComponent const nonStandardEntries = annotations.filter( a => bool(a.getCustomData('changeLogRemoved')) || this._annotationManager.isHidden(a.Id), ); - this._setAnnotationsOpacity(standardEntries, true); + this._readableRedactionsService.setAnnotationsOpacity(standardEntries, true); this._annotationManager.show(standardEntries); this._annotationManager.hide(nonStandardEntries); break; @@ -240,8 +235,8 @@ export class FilePreviewScreenComponent case ViewModes.DELTA: { const changeLogEntries = annotations.filter(a => bool(a.getCustomData('changeLog'))); const nonChangeLogEntries = annotations.filter(a => !bool(a.getCustomData('changeLog'))); - this._setAnnotationsColor(redactions, 'annotationColor'); - this._setAnnotationsOpacity(changeLogEntries, true); + this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor'); + this._readableRedactionsService.setAnnotationsOpacity(changeLogEntries, true); this._annotationManager.show(changeLogEntries); this._annotationManager.hide(nonChangeLogEntries); break; @@ -250,14 +245,8 @@ export class FilePreviewScreenComponent const nonRedactionEntries = annotations.filter( a => !bool(a.getCustomData('redaction')) || bool(a.getCustomData('changeLogRemoved')), ); - if (this._readableRedactionsService.active) { - this._setAnnotationsOpacity(redactions, true); - this._setAnnotationsColor(redactions, 'annotationColor'); - } else { - this._setAnnotationsOpacity(redactions); - this._setAnnotationsColor(redactions, 'redactionColor'); - } - + this._readableRedactionsService.setPreviewAnnotationsOpacity(redactions); + this._readableRedactionsService.setPreviewAnnotationsColor(redactions); this._annotationManager.show(redactions); this._annotationManager.hide(nonRedactionEntries); this._suggestionsService.hideSuggestionsInPreview(redactions); @@ -762,20 +751,6 @@ export class FilePreviewScreenComponent } } - private _setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal = false) { - annotations.forEach(annotation => { - annotation['Opacity'] = restoreToOriginal ? parseFloat(annotation.getCustomData('opacity')) : 1; - }); - } - - private _setAnnotationsColor(annotations: Annotation[], customData: string) { - annotations.forEach(annotation => { - const color = this._annotationDrawService.convertColor(annotation.getCustomData(customData)); - annotation['StrokeColor'] = color; - annotation['FillColor'] = color; - }); - } - private _navigateToDossier() { this._logger.info('Navigating to ', this.state.dossier.dossierName); return this._router.navigate([this.state.dossier.routerLink]); diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts index 0bc2609ae..ef04cce80 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/file-data.service.ts @@ -181,9 +181,10 @@ export class FileDataService extends EntitiesService { const redactionLogCopy = JSON.parse(JSON.stringify(redactionLog)); redactionLogCopy.redactionLogEntry = redactionLogCopy.redactionLogEntry.reduce((filtered, entry) => { - const isRemoveChange = entry.manualChanges.find(c => this.#isRemoveChange(c.manualRedactionType)); + const lastChange = entry.manualChanges.at(-1); + const isRemoveChange = this.#isRemoveChange(lastChange?.manualRedactionType); if (isRemoveChange) { - entry.manualChanges = entry.manualChanges.filter(c => !this.#isRemoveChange(c.manualRedactionType)); + entry.manualChanges.pop(); filtered.push(entry); } return filtered; diff --git a/apps/red-ui/src/app/modules/file-preview/services/suggestions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/suggestions.service.ts index 988f22ca0..429a4d2bd 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/suggestions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/suggestions.service.ts @@ -6,6 +6,7 @@ import Annotation = Core.Annotations.Annotation; import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service'; import { UserPreferenceService } from '@users/user-preference.service'; import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service'; +import { ReadableRedactionsService } from '../../pdf-viewer/services/readable-redactions.service'; @Injectable() export class SuggestionsService { @@ -15,6 +16,7 @@ export class SuggestionsService { private readonly _annotationManager: REDAnnotationManager, private readonly _userPreferenceService: UserPreferenceService, private readonly _annotationDrawService: AnnotationDrawService, + private readonly _readableRedactionsService: ReadableRedactionsService, ) {} set removedRedactions(removedRedactions: AnnotationWrapper[]) { @@ -39,14 +41,8 @@ export class SuggestionsService { #convertRemoveSuggestionsToRedactions(suggestions: Annotation[]): void { const removeSuggestions = suggestions.filter(a => bool(a.getCustomData('suggestionRemove'))); - - removeSuggestions.forEach(suggestion => { - const color = this._annotationDrawService.convertColor(suggestion.getCustomData('redactionColor')); - suggestion['Opacity'] = 1; - suggestion['StrokeColor'] = color; - suggestion['FillColor'] = color; - }); - + this._readableRedactionsService.setPreviewAnnotationsOpacity(removeSuggestions); + this._readableRedactionsService.setPreviewAnnotationsColor(removeSuggestions); this._annotationManager.show(removeSuggestions); } } diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts index 0cd30eccc..4833ce1e4 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts @@ -19,6 +19,7 @@ import Quad = Core.Math.Quad; const DEFAULT_TEXT_ANNOTATION_OPACITY = 1; const DEFAULT_REMOVED_ANNOTATION_OPACITY = 0.2; +const FINAL_REDACTION_COLOR = '#000000'; @Injectable() export class AnnotationDrawService { @@ -164,6 +165,7 @@ export class AnnotationDrawService { ? this._defaultColorsService.getColor(dossierTemplateId, 'requestAddColor') : this._defaultColorsService.getColor(dossierTemplateId, 'previewColor'); annotation.setCustomData('redactionColor', String(redactionColor)); + annotation.setCustomData('finalRedactionColor', FINAL_REDACTION_COLOR); annotation.setCustomData('annotationColor', String(annotationWrapper.color)); return annotation; diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts index cbc960487..0082f6ddc 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts @@ -80,6 +80,11 @@ export class REDDocumentViewer { return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast()); } + clearSelection() { + this.#document.clearSelection(); + this.#pdf.disable('textPopup'); + } + close() { this.#logger.info('[PDF] Closing document'); this.#document.closeDocument(); diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/readable-redactions.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/readable-redactions.service.ts index dbdf893b1..c98c4ba77 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/readable-redactions.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/readable-redactions.service.ts @@ -7,13 +7,15 @@ import { PdfViewer } from './pdf-viewer.service'; import { REDAnnotationManager } from './annotation-manager.service'; import { AnnotationDrawService } from './annotation-draw.service'; import { BehaviorSubject, Observable } from 'rxjs'; +import { Core } from '@pdftron/webviewer'; +import Annotation = Core.Annotations.Annotation; @Injectable() export class ReadableRedactionsService { + readonly active$: Observable; readonly #enableIcon = this._convertPath('/assets/icons/general/pdftron-action-enable-tooltips.svg'); readonly #disableIcon = this._convertPath('/assets/icons/general/pdftron-action-disable-tooltips.svg'); - readonly #active$ = new BehaviorSubject(false); - readonly active$: Observable; + readonly #active$ = new BehaviorSubject(true); constructor( @Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn, @@ -47,4 +49,27 @@ export class ReadableRedactionsService { img: this.toggleReadableRedactionsBtnIcon, }); } + + setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal = false) { + annotations.forEach(annotation => { + annotation['Opacity'] = restoreToOriginal ? parseFloat(annotation.getCustomData('opacity')) : 0.5; + }); + } + + setAnnotationsColor(annotations: Annotation[], customData: string) { + annotations.forEach(annotation => { + const color = this._annotationDrawService.convertColor(annotation.getCustomData(customData)); + annotation['StrokeColor'] = color; + annotation['FillColor'] = color; + }); + } + + setPreviewAnnotationsOpacity(annotations: Annotation[]) { + this.setAnnotationsOpacity(annotations, !this.active); + } + + setPreviewAnnotationsColor(annotations: Annotation[]) { + const color = this.active ? 'redactionColor' : 'finalRedactionColor'; + this.setAnnotationsColor(annotations, color); + } } diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts index 8cfb3cf89..bc905600b 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/viewer-header.service.ts @@ -249,7 +249,7 @@ export class ViewerHeaderService { header.getItems().splice(10, header.getItems().length - 14, ...enabledItems); }); - this._pdf.instance.UI.updateElement('selectToolButton', { + this._pdf.instance?.UI.updateElement('selectToolButton', { img: this._convertPath('/assets/icons/general/pdftron-cursor.svg'), }); } diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts index 345d3e39f..421d67be3 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/download-package/edit-dossier-download-package.component.ts @@ -106,8 +106,7 @@ export class EditDossierDownloadPackageComponent existsWatermarks: this.#existsWatermarks$, }); - this.availableReportTypes = - (await firstValueFrom(this._reportTemplateController.getAvailableReportTemplates(dossierTemplateId))) || []; + this.availableReportTypes = (await this._reportTemplateController.getAvailableReportTemplates(dossierTemplateId)) || []; this.form = this._getForm(); if (!this.canEditDossier) { diff --git a/apps/red-ui/src/app/modules/shared/components/add-edit-entity/add-edit-entity.component.ts b/apps/red-ui/src/app/modules/shared/components/add-edit-entity/add-edit-entity.component.ts index 6581790e3..5cf023bb0 100644 --- a/apps/red-ui/src/app/modules/shared/components/add-edit-entity/add-edit-entity.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/add-edit-entity/add-edit-entity.component.ts @@ -200,7 +200,7 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit private _toTechnicalName(value: string) { const existingTechnicalNames = this._dictionariesMapService.get(this.dossierTemplateId).map(dict => dict.type); const baseTechnicalName = toSnakeCase(value.trim()); - let technicalName = baseTechnicalName; + let technicalName = baseTechnicalName.replaceAll(/[^A-Za-z0-9_-]/g, ''); let suffix = 1; while (existingTechnicalNames.includes(technicalName)) { technicalName = [baseTechnicalName, suffix++].join('_'); diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts index 0c9f3f8d9..c180328bd 100644 --- a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts @@ -2,8 +2,14 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/c import { PermissionsService } from '@services/permissions.service'; import { Dossier, File } from '@red/domain'; import { FileDownloadService } from '@upload-download/services/file-download.service'; -import { CircleButtonType, CircleButtonTypes, Toaster } from '@iqser/common-ui'; +import { CircleButtonType, CircleButtonTypes, defaultDialogConfig, Toaster } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { MatDialog } from '@angular/material/dialog'; +import { + DownloadDialogComponent, + DownloadDialogData, + DownloadDialogResult, +} from '@shared/dialogs/download-dialog/download-dialog.component'; import { firstValueFrom } from 'rxjs'; @Component({ @@ -25,6 +31,7 @@ export class FileDownloadBtnComponent implements OnChanges { constructor( private readonly _permissionsService: PermissionsService, private readonly _fileDownloadService: FileDownloadService, + private readonly _dialog: MatDialog, private readonly _toaster: Toaster, ) {} @@ -35,9 +42,20 @@ export class FileDownloadBtnComponent implements OnChanges { async downloadFiles($event: MouseEvent) { $event.stopPropagation(); - const dossierId = this.files[0].dossierId; - const filesIds = this.files.map(f => f.id); - await firstValueFrom(this._fileDownloadService.downloadFiles(filesIds, dossierId)); + const ref = this._dialog.open(DownloadDialogComponent, { + ...defaultDialogConfig, + data: { dossier: this.dossier, hasUnapprovedDocuments: this.files.some(file => !file.isApproved) }, + }); + const result = await firstValueFrom(ref.afterClosed()); + if (!result) { + return; + } + + await this._fileDownloadService.downloadFiles({ + dossierId: this.dossier.id, + fileIds: this.files.map(f => f.id), + ...result, + }); this._toaster.info(_('download-status.queued')); } } diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts index 7dc7730c1..330b75e3c 100644 --- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.ts @@ -1,7 +1,8 @@ import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core'; -import { Action, ActionTypes, File } from '@red/domain'; +import { Action, ActionTypes, Dossier, File } from '@red/domain'; import { CircleButtonType, + defaultDialogConfig, IqserTooltipPosition, OverlappingElements, ScrollableParentView, @@ -13,6 +14,12 @@ import { FileDownloadService } from '@upload-download/services/file-download.ser import { PermissionsService } from '@services/permissions.service'; import { firstValueFrom } from 'rxjs'; import { MatMenuTrigger } from '@angular/material/menu'; +import { + DownloadDialogComponent, + DownloadDialogData, + DownloadDialogResult, +} from '@shared/dialogs/download-dialog/download-dialog.component'; +import { MatDialog } from '@angular/material/dialog'; @Component({ selector: 'redaction-expandable-file-actions', @@ -39,8 +46,13 @@ export class ExpandableFileActionsComponent implements OnChanges { private readonly _fileDownloadService: FileDownloadService, private readonly _toaster: Toaster, private readonly _permissionsService: PermissionsService, + private readonly _dialog: MatDialog, ) {} + get overlappingElement() { + return this.helpModeKey === 'document_features_in_editor' ? OverlappingElements.USER_MENU : undefined; + } + ngOnChanges(changes: SimpleChanges) { if (changes.actions || changes.maxWidth || changes.minWidth) { let count = 0; @@ -72,7 +84,7 @@ export class ExpandableFileActionsComponent implements OnChanges { // Patch download button const downloadBtn = this.actions.find(btn => btn.type === ActionTypes.downloadBtn); if (downloadBtn) { - downloadBtn.action = ($event: MouseEvent) => this._downloadFiles($event, downloadBtn.files); + downloadBtn.action = ($event: MouseEvent) => this._downloadFiles($event, downloadBtn.files, downloadBtn.dossier); downloadBtn.disabled = !this._permissionsService.canDownloadFiles(downloadBtn.files, downloadBtn.dossier); } } @@ -83,20 +95,27 @@ export class ExpandableFileActionsComponent implements OnChanges { } } - private async _downloadFiles($event: MouseEvent, files: File[]) { - $event.stopPropagation(); - const dossierId = files[0].dossierId; - const filesIds = files.map(f => f.id); - await firstValueFrom(this._fileDownloadService.downloadFiles(filesIds, dossierId)); - this._toaster.info(_('download-status.queued')); - } - onButtonClick(button: Action, $event: MouseEvent) { button.action($event); this.matMenu.closeMenu(); } - get overlappingElement() { - return this.helpModeKey === 'document_features_in_editor' ? OverlappingElements.USER_MENU : undefined; + private async _downloadFiles($event: MouseEvent, files: File[], dossier: Dossier) { + $event.stopPropagation(); + const ref = this._dialog.open(DownloadDialogComponent, { + ...defaultDialogConfig, + data: { dossier, hasUnapprovedDocuments: files.some(file => !file.isApproved) }, + }); + const result = await firstValueFrom(ref.afterClosed()); + if (!result) { + return; + } + + await this._fileDownloadService.downloadFiles({ + dossierId: dossier.id, + fileIds: files.map(f => f.id), + ...result, + }); + this._toaster.info(_('download-status.queued')); } } diff --git a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss index 8eb4edefd..5860f0d48 100644 --- a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss +++ b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss @@ -25,6 +25,7 @@ flex-direction: column; overflow-x: hidden; overflow-y: auto; + flex-wrap: nowrap; @include common-mixins.scroll-bar(); } } diff --git a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts index b711e6ea0..fa1f2ce77 100644 --- a/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/shared/dialogs/add-dossier-dialog/add-dossier-dialog.component.ts @@ -102,7 +102,7 @@ export class AddDossierDialogComponent extends BaseDialogComponent implements On if (dossierTemplate) { this.availableReportTypes = - (await firstValueFrom(this._reportTemplateController.getAvailableReportTemplates(dossierTemplate.dossierTemplateId))) || []; + (await this._reportTemplateController.getAvailableReportTemplates(dossierTemplate.dossierTemplateId)) || []; // update dropdown values this.form.patchValue( { diff --git a/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.html b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.html new file mode 100644 index 000000000..ce6318b1a --- /dev/null +++ b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.html @@ -0,0 +1,61 @@ +
+
+
+ +
+
+
+ +
+ + + + +
+ + +
+ +
+
+
+
+ +
+ + +
+
+ + + {{ option.fileName }} {{ option.multiFileReport ? ('reports-screen.multi-file-report' | translate) : '' }} + diff --git a/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.scss b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts new file mode 100644 index 000000000..d702d3300 --- /dev/null +++ b/apps/red-ui/src/app/modules/shared/dialogs/download-dialog/download-dialog.component.ts @@ -0,0 +1,100 @@ +import { Component, Inject } from '@angular/core'; +import { Dossier, DownloadFileType, IReportTemplate } from '@red/domain'; +import { downloadTypesForDownloadTranslations } from '@translations/download-types-translations'; +import { ReportTemplateService } from '@services/report-template.service'; +import { AbstractControl, FormBuilder } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { DefaultColorsService } from '@services/entity-services/default-colors.service'; +import { List } from '@iqser/common-ui'; + +export interface DownloadDialogData { + readonly dossier: Dossier; + readonly hasUnapprovedDocuments: boolean; +} + +export interface DownloadDialogResult { + downloadFileTypes: List; + reportTemplateIds: List; + redactionPreviewColor: string; +} + +@Component({ + selector: 'redaction-download-dialog', + templateUrl: './download-dialog.component.html', + styleUrls: ['./download-dialog.component.scss'], +}) +export class DownloadDialogComponent { + readonly downloadTypes: { key: DownloadFileType; label: string }[] = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map( + (type: DownloadFileType) => ({ + key: type, + label: downloadTypesForDownloadTranslations[type], + }), + ); + readonly availableReportTypes = this._availableReportTypes; + readonly form = this._getForm(); + + constructor( + private readonly _defaultColorsService: DefaultColorsService, + private readonly _reportTemplateController: ReportTemplateService, + private readonly _formBuilder: FormBuilder, + private readonly _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: DownloadDialogData, + ) {} + + get reportTypesLength() { + return this.form.controls.reportTemplateIds?.value?.length || 0; + } + + get downloadFileTypesLength() { + return this.form.controls.downloadFileTypes?.value?.length || 0; + } + + private get _availableReportTypes() { + const dossierTemplateId = this.data.dossier.dossierTemplateId; + const result = this._reportTemplateController.getAvailableReportTemplates(dossierTemplateId); + return result.then(values => values ?? []); + } + + reportTemplateValueMapper = (reportTemplate: IReportTemplate) => reportTemplate.templateId; + + async save() { + const result: DownloadDialogResult = { + reportTemplateIds: this.form.controls.reportTemplateIds.value, + downloadFileTypes: this.form.controls.downloadFileTypes.value, + redactionPreviewColor: this.form.controls.redactionPreviewColor.value, + }; + + this._dialogRef.close(result); + } + + close() { + this._dialogRef.close(); + } + + private _hasReportTemplateOrDownloadType(control: AbstractControl) { + const atLeastAReportSelected = control.get('reportTemplateIds')?.value.length > 0; + const atLeastATypeSelected = control.get('downloadFileTypes')?.value.length > 0; + + return atLeastATypeSelected || atLeastAReportSelected ? null : { reportTemplateIds: true, downloadFileTypes: true }; + } + + private _getForm() { + const previewColor = this._defaultColorsService.getColor(this.data.dossier.dossierTemplateId, 'previewColor'); + return this._formBuilder.group( + { + reportTemplateIds: [this.data.dossier.reportTemplateIds], + downloadFileTypes: [this.data.dossier.downloadFileTypes], + redactionPreviewColor: [previewColor], + }, + { + validators: [control => this._hasReportTemplateOrDownloadType(control), control => this._isHexColor(control)], + }, + ); + } + + private _isHexColor(control: AbstractControl) { + const color = control.get('redactionPreviewColor')?.value; + const isHexColor = /^#[0-9A-F]{6}$/i.test(color); + return isHexColor ? null : { redactionPreviewColor: true }; + } +} diff --git a/apps/red-ui/src/app/modules/shared/shared.module.ts b/apps/red-ui/src/app/modules/shared/shared.module.ts index d6ae41945..1ceebe874 100644 --- a/apps/red-ui/src/app/modules/shared/shared.module.ts +++ b/apps/red-ui/src/app/modules/shared/shared.module.ts @@ -42,6 +42,7 @@ import { AddEditEntityComponent } from './components/add-edit-entity/add-edit-en import { ColorPickerModule } from 'ngx-color-picker'; import { WatermarkSelectorComponent } from './components/dossier-watermark-selector/watermark-selector.component'; import { OcrProgressBarComponent } from './components/ocr-progress-bar/ocr-progress-bar.component'; +import { DownloadDialogComponent } from './dialogs/download-dialog/download-dialog.component'; const buttons = [FileDownloadBtnComponent]; @@ -76,7 +77,7 @@ const services = [SharedDialogService]; const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, ColorPickerModule]; @NgModule({ - declarations: [...components, ...utils, EditorComponent], + declarations: [...components, ...utils, EditorComponent, DownloadDialogComponent], imports: [ CommonModule, ...modules, diff --git a/apps/red-ui/src/app/modules/upload-download/services/file-download.service.ts b/apps/red-ui/src/app/modules/upload-download/services/file-download.service.ts index 18a766f97..7bb2ea402 100644 --- a/apps/red-ui/src/app/modules/upload-download/services/file-download.service.ts +++ b/apps/red-ui/src/app/modules/upload-download/services/file-download.service.ts @@ -29,11 +29,9 @@ export class FileDownloadService extends EntitiesService { - return this.prepareDownload({ - fileIds, - dossierId, - }).pipe(switchMap(() => this.loadAll())); + downloadFiles(request: IPrepareDownloadRequest): Promise { + const prepare = this.prepareDownload(request).pipe(switchMap(() => this.loadAll())); + return firstValueFrom(prepare); } loadAll(): Observable { @@ -61,7 +59,7 @@ export class FileDownloadService extends EntitiesService { - return this._post(body, `${this._defaultModelPath}/prepare`); + return this._post(body, `${this._defaultModelPath}/prepare-option`); } @Validate() diff --git a/apps/red-ui/src/app/services/files/rss.service.ts b/apps/red-ui/src/app/services/files/rss.service.ts index 1d4b14d5e..25f60ea1d 100644 --- a/apps/red-ui/src/app/services/files/rss.service.ts +++ b/apps/red-ui/src/app/services/files/rss.service.ts @@ -40,6 +40,20 @@ export class RssService extends GenericService { }); } + @Validate() + override( + @RequiredParam() dossierId: string, + @RequiredParam() fileId: string, + @RequiredParam() componentOverrides: Record, + ) { + return this._post({ componentOverrides }, `rss/override/${dossierId}/${fileId}`); + } + + @Validate() + revertOverride(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, @RequiredParam() components: string[]) { + return this._post({ components }, `rss/override/revert/${dossierId}/${fileId}`); + } + exportJSON(dossierId: string, fileId: string, name: string) { return this.getRSSData(dossierId, fileId).pipe( tap(data => { diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index 86d6a4964..f82d8483e 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -261,7 +261,7 @@ export class PermissionsService { if (files.length === 0) { return false; } - return this.isApprover(dossier) && files.reduce((prev, file) => prev && file.isApproved, true); + return this.isApprover(dossier) && files.reduce((prev, file) => prev && !file.isInitialProcessing, true); } canSoftDeleteDossier(dossier: IDossier): boolean { diff --git a/apps/red-ui/src/app/services/report-template.service.ts b/apps/red-ui/src/app/services/report-template.service.ts index e58247d34..2ed205399 100644 --- a/apps/red-ui/src/app/services/report-template.service.ts +++ b/apps/red-ui/src/app/services/report-template.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { GenericService, HeadersConfiguration, RequiredParam, Validate } from '@iqser/common-ui'; import { IPlaceholdersResponse, IReportTemplate } from '@red/domain'; -import { Observable, of } from 'rxjs'; +import { firstValueFrom, Observable, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; import { catchError } from 'rxjs/operators'; @@ -34,7 +34,8 @@ export class ReportTemplateService extends GenericService { @Validate() getAvailableReportTemplates(@RequiredParam() dossierTemplateId: string) { - return this.getAll(`${this._defaultModelPath}/${dossierTemplateId}`); + const request = this.getAll(`${this._defaultModelPath}/${dossierTemplateId}`); + return firstValueFrom(request); } @Validate() diff --git a/apps/red-ui/src/app/translations/download-types-translations.ts b/apps/red-ui/src/app/translations/download-types-translations.ts index ae98277c7..e11ea04a9 100644 --- a/apps/red-ui/src/app/translations/download-types-translations.ts +++ b/apps/red-ui/src/app/translations/download-types-translations.ts @@ -9,3 +9,12 @@ export const downloadTypesTranslations: { [key in DownloadFileType]: string } = FLATTEN: _('download-type.flatten'), DELTA_PREVIEW: _('download-type.delta-preview'), } as const; + +export const downloadTypesForDownloadTranslations: { [key in DownloadFileType]: string } = { + ORIGINAL: _('download-type.original'), + PREVIEW: _('download-type.preview'), + REDACTED: _('download-type.redacted-only'), + ANNOTATED: _('download-type.annotated'), + FLATTEN: _('download-type.flatten'), + DELTA_PREVIEW: _('download-type.delta-preview'), +} as const; diff --git a/apps/red-ui/src/app/users/red-role.guard.ts b/apps/red-ui/src/app/users/red-role.guard.ts index 2d7dfc9b5..7a5b156ec 100644 --- a/apps/red-ui/src/app/users/red-role.guard.ts +++ b/apps/red-ui/src/app/users/red-role.guard.ts @@ -13,7 +13,7 @@ export class RedRoleGuard extends IqserRoleGuard { async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { const currentUser = this._userService.currentUser; - if (!currentUser.hasAnyRole) { + if (!currentUser?.hasAnyRole) { await this._router.navigate(['/auth-error']); this._loadingService.stop(); return false; diff --git a/apps/red-ui/src/assets/i18n/redact/de.json b/apps/red-ui/src/assets/i18n/redact/de.json index 6780b082f..1045a6ad2 100644 --- a/apps/red-ui/src/assets/i18n/redact/de.json +++ b/apps/red-ui/src/assets/i18n/redact/de.json @@ -1017,6 +1017,17 @@ "active": "", "archive": "" }, + "download-dialog": { + "actions": { + "save": "" + }, + "form": { + "redaction-preview-color": "", + "redaction-preview-color-placeholder": "" + }, + "header": "", + "unapproved-files-warning": "" + }, "download-includes": "Wählen Sie die Dokumente für Ihr Download-Paket aus", "download-status": { "queued": "Ihr Download wurde zur Warteschlange hinzugefügt. Hier finden Sie alle angeforderten Downloads: My Downloads." @@ -1028,7 +1039,8 @@ "label": "{length} Dokumenten{length, plural, one{version} other{versionen}}", "original": "Optimiertes PDF", "preview": "PDF-Vorschau", - "redacted": "geschwärztes PDF" + "redacted": "geschwärztes PDF", + "redacted-only": "" }, "downloads-list": { "actions": { @@ -1941,9 +1953,13 @@ }, "rss-dialog": { "actions": { + "cancel-edit": "", "close": "", + "edit": "", "export-json": "", - "export-xml": "" + "export-xml": "", + "save": "", + "undo": "" }, "title": "" }, diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json index 79bb30582..4ba768210 100644 --- a/apps/red-ui/src/assets/i18n/redact/en.json +++ b/apps/red-ui/src/assets/i18n/redact/en.json @@ -849,7 +849,7 @@ } }, "download-file": "Download", - "download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be approved in order to download.", + "download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be initially processed in order to download.", "file-listing": { "file-entry": { "file-error": "Re-processing required", @@ -1017,6 +1017,17 @@ "active": "Active", "archive": "Archived" }, + "download-dialog": { + "actions": { + "save": "Download" + }, + "form": { + "redaction-preview-color": "Redaction preview color", + "redaction-preview-color-placeholder": "#000000" + }, + "header": "Download options", + "unapproved-files-warning": "This download contains unapproved file(s)" + }, "download-includes": "Choose what is included at download:", "download-status": { "queued": "Your download has been queued, you can see all your requested downloads here: My Downloads." @@ -1028,7 +1039,8 @@ "label": "{length} document {length, plural, one{version} other{versions}}", "original": "Optimized PDF", "preview": "Preview PDF", - "redacted": "Redacted PDF" + "redacted": "Redacted PDF", + "redacted-only": "Redacted PDF (approved documents only)" }, "downloads-list": { "actions": { @@ -1941,9 +1953,13 @@ }, "rss-dialog": { "actions": { + "cancel-edit": "Cancel", "close": "Close", + "edit": "Edit", "export-json": "Export JSON", - "export-xml": "Export XML" + "export-xml": "Export XML", + "save": "Save", + "undo": "Undo" }, "title": "Structured Component Management" }, diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json index 4338fded3..2e29848a1 100644 --- a/apps/red-ui/src/assets/i18n/scm/de.json +++ b/apps/red-ui/src/assets/i18n/scm/de.json @@ -1017,6 +1017,17 @@ "active": "", "archive": "" }, + "download-dialog": { + "actions": { + "save": "" + }, + "form": { + "redaction-preview-color": "", + "redaction-preview-color-placeholder": "" + }, + "header": "", + "unapproved-files-warning": "" + }, "download-includes": "Wählen Sie die Dokumente für Ihr Download-Paket aus", "download-status": { "queued": "Ihr Download wurde zur Warteschlange hinzugefügt. Hier finden Sie alle angeforderten Downloads: My Downloads." @@ -1028,7 +1039,8 @@ "label": "{length} Dokumenten{length, plural, one{version} other{versionen}}", "original": "Optimiertes PDF", "preview": "PDF-Vorschau", - "redacted": "geschwärztes PDF" + "redacted": "geschwärztes PDF", + "redacted-only": "" }, "downloads-list": { "actions": { @@ -1941,9 +1953,13 @@ }, "rss-dialog": { "actions": { + "cancel-edit": "", "close": "", + "edit": "", "export-json": "", - "export-xml": "" + "export-xml": "", + "save": "", + "undo": "" }, "title": "" }, diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json index d3360d811..e4c0f2681 100644 --- a/apps/red-ui/src/assets/i18n/scm/en.json +++ b/apps/red-ui/src/assets/i18n/scm/en.json @@ -849,7 +849,7 @@ } }, "download-file": "Download", - "download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be approved in order to download.", + "download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be initially processed in order to download.", "file-listing": { "file-entry": { "file-error": "Re-processing required", @@ -1017,6 +1017,17 @@ "active": "Active", "archive": "Archived" }, + "download-dialog": { + "actions": { + "save": "Download" + }, + "form": { + "redaction-preview-color": "Redaction preview color", + "redaction-preview-color-placeholder": "#000000" + }, + "header": "Download options", + "unapproved-files-warning": "This download contains unapproved file(s)" + }, "download-includes": "Choose what is included at download:", "download-status": { "queued": "Your download has been queued, you can see all your requested downloads here: My Downloads." @@ -1028,7 +1039,8 @@ "label": "{length} document {length, plural, one{version} other{versions}}", "original": "Optimized PDF", "preview": "Preview PDF", - "redacted": "Redacted PDF" + "redacted": "Redacted PDF", + "redacted-only": "Redacted PDF (redacted documents only)" }, "downloads-list": { "actions": { @@ -1941,9 +1953,13 @@ }, "rss-dialog": { "actions": { + "cancel-edit": "Cancel", "close": "Close", + "edit": "Edit", "export-json": "Export JSON", - "export-xml": "Export XML" + "export-xml": "Export XML", + "save": "Save", + "undo": "Undo" }, "title": "Structured Component Management" }, diff --git a/libs/common-ui b/libs/common-ui index 5f9c754ab..b58f1ca2f 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 5f9c754abf0fb4fcc0606d811a66c5bbb88d164a +Subproject commit b58f1ca2fed8e8ddf2834c0fd15e48d00b6a1329 diff --git a/libs/red-domain/src/lib/downloads/prepare-download.request.ts b/libs/red-domain/src/lib/downloads/prepare-download.request.ts index cade875c9..22f7295ef 100644 --- a/libs/red-domain/src/lib/downloads/prepare-download.request.ts +++ b/libs/red-domain/src/lib/downloads/prepare-download.request.ts @@ -2,8 +2,12 @@ * Object containing information on which file and report types should be included in the download. */ import { List } from '@iqser/common-ui'; +import { DownloadFileType } from '../shared'; export interface IPrepareDownloadRequest { - readonly dossierId?: string; - readonly fileIds?: List; + readonly dossierId: string; + readonly fileIds: List; + readonly reportTemplateIds: List; + readonly downloadFileTypes: List; + readonly redactionPreviewColor: string; } diff --git a/libs/red-domain/src/lib/files/file.model.ts b/libs/red-domain/src/lib/files/file.model.ts index de62eaf4d..683eb6b9f 100644 --- a/libs/red-domain/src/lib/files/file.model.ts +++ b/libs/red-domain/src/lib/files/file.model.ts @@ -146,7 +146,11 @@ export class File extends Entity implements IFile { this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL; this.canBeApproved = !this.hasSuggestions && !this.isProcessing && !this.isError; this.canBeOpened = !this.isError && !this.isUnprocessed && this.numberOfAnalyses > 0; - this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isNew || this.isUnderReview || this.isUnderApproval); + this.canBeOCRed = + !this.excluded && + !this.lastOCRTime && + this.numberOfAnalyses !== 0 && + (this.isNew || this.isUnderReview || this.isUnderApproval); this.fileAttributes = file.fileAttributes && file.fileAttributes.attributeIdToValue ? file.fileAttributes : { attributeIdToValue: {} }; diff --git a/package-lock.json b/package-lock.json index d848c7897..1f62cce76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.813.0", + "version": "3.828.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 8dd5cb2ab..fd7578f43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.813.0", + "version": "3.828.0", "private": true, "license": "MIT", "scripts": { diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index 25eb9ec3e..ad56665b1 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ