diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts index 4322e548d..775e2cc5c 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts @@ -168,7 +168,6 @@ export class EditDossierDialogComponent extends BaseDialogComponent implements A } this.activeNav = key; } - this._waitingForConfirmation = false; }); } else { this.activeNav = key; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/dossier-overview-screen-header.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/dossier-overview-screen-header.component.html index 89d79f89b..57de5f509 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/dossier-overview-screen-header.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/components/screen-header/dossier-overview-screen-header.component.html @@ -23,7 +23,7 @@ (); readonly circleButtonTypes = CircleButtonTypes; actionConfigs: List; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html index 2a26b54d5..020770b34 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.html @@ -1,8 +1,7 @@ -
+
diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts index a8e088dab..34837c093 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/screen/dossier-overview-screen.component.ts @@ -39,7 +39,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { FileAttributesService } from '@services/entity-services/file-attributes.service'; import { ConfigService } from '../config.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; -import { LongPressEvent } from '@shared/directives/long-press.directive'; import { UserPreferenceService } from '@services/user-preference.service'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesService } from '@services/entity-services/files.service'; @@ -68,7 +67,6 @@ export class DossierOverviewScreenComponent extends ListingComponent imple collapsedDetails = false; dossierAttributes: DossierAttributeWithValue[] = []; tableColumnConfigs: readonly TableColumnConfig[]; - analysisForced: boolean; displayedInFileListAttributes: IFileAttributeConfig[] = []; displayedAttributes: IFileAttributeConfig[] = []; readonly workflowConfig: WorkflowConfig = this.configService.workflowConfig; @@ -178,10 +176,6 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._tableComponent?.scrollToLastIndex(); } - forceReanalysisAction($event: LongPressEvent) { - this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled; - } - @HostListener('drop', ['$event']) onDrop(event: DragEvent): void { const currentDossier = this._dossiersService.find(this.dossierId); diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts index dfdc3e696..aeeaf658a 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview/services/bulk-actions.service.ts @@ -115,14 +115,23 @@ export class BulkActionsService { } async approve(files: File[]): Promise { + const foundAnalysisRequiredFile = files.find(file => file.analysisRequired); const foundUpdatedFile = files.find(file => file.hasUpdates); - if (foundUpdatedFile) { + if (foundAnalysisRequiredFile || foundUpdatedFile) { this._dialogService.openDialog( 'confirm', null, new ConfirmationDialogInput({ - title: _('confirmation-dialog.approve-multiple-files.title'), - question: _('confirmation-dialog.approve-multiple-files.question'), + title: foundAnalysisRequiredFile + ? _('confirmation-dialog.approve-multiple-files-without-analysis.title') + : _('confirmation-dialog.approve-multiple-files.title'), + question: foundAnalysisRequiredFile + ? _('confirmation-dialog.approve-multiple-files-without-analysis.question') + : _('confirmation-dialog.approve-multiple-files.question'), + confirmationText: foundAnalysisRequiredFile + ? _('confirmation-dialog.approve-multiple-files-without-analysis.confirmationText') + : null, + denyText: foundAnalysisRequiredFile ? _('confirmation-dialog.approve-multiple-files-without-analysis.denyText') : null, }), async () => { this._loadingService.start(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html index cd75fe63c..cc48bfa09 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html @@ -16,8 +16,8 @@ (click)="multiSelectService.activate()" *ngIf="(multiSelectService.enabled$ | async) && (multiSelectInactive$ | async)" class="all-caps-label primary pointer" - translate="file-preview.tabs.annotations.select" iqserHelpMode="bulk_select_annotations" + translate="file-preview.tabs.annotations.select" > -
@@ -159,18 +153,18 @@ [class.active-panel]="!pagesPanelActive" [hidden]="excludedPagesService.shown$ | async" class="annotations" + id="annotations-list" iqserHasScrollbar tabindex="1" - id="annotations-list" > - + {{ 'file-preview.tabs.annotations.page-is' | translate }} . + + + {{ 'file-preview.tabs.annotations.wrong-filters' | translate }} + + {{ 'file-preview.tabs.annotations.the-filters' | translate }} +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts index c3fd3396e..49e0d9989 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts @@ -34,6 +34,7 @@ import { MultiSelectService } from '../../services/multi-select.service'; import { DocumentInfoService } from '../../services/document-info.service'; import { SkippedService } from '../../services/skipped.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -47,6 +48,8 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; export class FileWorkloadComponent { readonly iconButtonTypes = IconButtonTypes; readonly circleButtonTypes = CircleButtonTypes; + readonly noDataI18NKey = _('file-preview.no-data.title'); + readonly resetFiltersI18NKey = _('file-preview.reset-filters'); displayedAnnotations = new Map(); @Input() selectedAnnotations: AnnotationWrapper[]; @@ -72,14 +75,14 @@ export class FileWorkloadComponent { @ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef; constructor( - readonly excludedPagesService: ExcludedPagesService, - readonly multiSelectService: MultiSelectService, - readonly documentInfoService: DocumentInfoService, + readonly filterService: FilterService, readonly skippedService: SkippedService, readonly state: FilePreviewStateService, - private readonly _permissionsService: PermissionsService, + readonly multiSelectService: MultiSelectService, + readonly documentInfoService: DocumentInfoService, + readonly excludedPagesService: ExcludedPagesService, private readonly _changeDetectorRef: ChangeDetectorRef, - private readonly _filterService: FilterService, + private readonly _permissionsService: PermissionsService, private readonly _annotationProcessingService: AnnotationProcessingService, ) { this.displayedAnnotations$ = this._displayedAnnotations$; @@ -138,14 +141,18 @@ export class FileWorkloadComponent { } private get _displayedAnnotations$(): Observable> { - const primary$ = this._filterService.getFilterModels$('primaryFilters'); - const secondary$ = this._filterService.getFilterModels$('secondaryFilters'); + const primary$ = this.filterService.getFilterModels$('primaryFilters'); + const secondary$ = this.filterService.getFilterModels$('secondaryFilters'); return combineLatest([this._annotations$.asObservable(), primary$, secondary$]).pipe( map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary)), ); } + get #allPages() { + return Array.from({ length: this.file?.numberOfPages }, (x, i) => i + 1); + } + private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') { if (elements.length > 0) { scrollIntoView(elements[0], { @@ -335,11 +342,26 @@ export class FileWorkloadComponent { secondary: INestedFilter[] = [], ): Map { if (!primary || primary.length === 0) { - this.displayedPages = Array.from({ length: this.file?.numberOfPages }, (x, i) => i + 1); + this.displayedPages = this.#allPages; return; } + this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(annotations, primary, secondary); - this.displayedPages = [...this.displayedAnnotations.keys()]; + const pagesThatDisplayAnnotations = [...this.displayedAnnotations.keys()]; + const enabledFilters = this.filterService.enabledFlatFilters; + if (enabledFilters.some(f => f.id === 'pages-without-annotations')) { + if (enabledFilters.length === 1) { + this.displayedPages = this.#allPages.filter(page => !pagesThatDisplayAnnotations.includes(page)); + } else { + this.displayedPages = []; + } + this.displayedAnnotations.clear(); + } else if (enabledFilters.length) { + this.displayedPages = pagesThatDisplayAnnotations; + } else { + this.displayedPages = this.#allPages; + } + return this.displayedAnnotations; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts index 509e9af3e..2df092289 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts @@ -435,10 +435,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } async downloadOriginalFile(file: File) { - const data = await this._fileManagementService - .downloadOriginalFile(this.dossierId, this.fileId, 'response', file.cacheIdentifier) - .toPromise(); - download(data, file.filename); + const originalFile = this._fileManagementService.downloadOriginalFile( + this.dossierId, + this.fileId, + 'response', + file.cacheIdentifier, + ); + download(await firstValueFrom(originalFile), file.filename); } #deactivateMultiSelect(): void { diff --git a/apps/red-ui/src/app/modules/dossier/services/annotation-processing.service.ts b/apps/red-ui/src/app/modules/dossier/services/annotation-processing.service.ts index 5c1ea0c65..06bbf924f 100644 --- a/apps/red-ui/src/app/modules/dossier/services/annotation-processing.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/annotation-processing.service.ts @@ -9,6 +9,7 @@ import { IViewedPage } from '@red/domain'; @Injectable() export class AnnotationProcessingService { static secondaryAnnotationFilters(viewedPages: IViewedPage[]): INestedFilter[] { + const _viewedPages = viewedPages.map(page => page.page); return [ { id: 'with-comments', @@ -32,7 +33,15 @@ export class AnnotationProcessingService { label: _('filter-menu.unseen-pages'), checked: false, topLevelFilter: true, - checker: (annotation: AnnotationWrapper) => !viewedPages.map(page => page.page).includes(annotation.pageNumber), + checker: (annotation: AnnotationWrapper) => !_viewedPages.includes(annotation.pageNumber), + }, + { + id: 'pages-without-annotations', + icon: 'iqser:pages', + label: _('filter-menu.pages-without-annotations'), + checked: false, + topLevelFilter: true, + checker: () => true, }, ].map(item => new NestedFilter(item)); } diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index 94f197511..d1f53443b 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -205,10 +205,19 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, icon: 'red:stop', show: this.canDisableAutoAnalysis, }, + { + type: ActionTypes.circleBtn, + action: $event => this._reanalyseFile($event), + tooltip: _('file-preview.reanalyse-notification'), + tooltipClass: 'warn small', + icon: 'iqser:refresh', + show: this.showReanalyseFilePreview, + }, { type: ActionTypes.circleBtn, action: $event => this.toggleAutomaticAnalysis($event), tooltip: _('dossier-overview.enable-auto-analysis'), + buttonType: this.isFilePreview ? CircleButtonTypes.warn : CircleButtonTypes.default, icon: 'red:play', show: this.canEnableAutoAnalysis, }, @@ -226,15 +235,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, icon: 'iqser:ocr', show: this.showOCR, }, - { - type: ActionTypes.circleBtn, - action: $event => this._reanalyseFile($event), - tooltip: _('file-preview.reanalyse-notification'), - buttonType: CircleButtonTypes.warn, - tooltipClass: 'warn small', - icon: 'iqser:refresh', - show: this.showReanalyseFilePreview, - }, { type: ActionTypes.circleBtn, action: $event => this._reanalyseFile($event), @@ -267,7 +267,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, async setFileApproved($event: MouseEvent) { $event.stopPropagation(); - if (!this.file.hasUpdates) { + if (!this.file.analysisRequired && !this.file.hasUpdates) { await this._setFileApproved(); return; } @@ -276,8 +276,16 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, 'confirm', $event, new ConfirmationDialogInput({ - title: _('confirmation-dialog.approve-file.title'), - question: _('confirmation-dialog.approve-file.question'), + title: this.file.analysisRequired + ? _('confirmation-dialog.approve-file-without-analysis.title') + : _('confirmation-dialog.approve-file.title'), + question: this.file.analysisRequired + ? _('confirmation-dialog.approve-file-without-analysis.question') + : _('confirmation-dialog.approve-file.question'), + confirmationText: this.file.analysisRequired + ? _('confirmation-dialog.approve-file-without-analysis.confirmationText') + : null, + denyText: this.file.analysisRequired ? _('confirmation-dialog.approve-file-without-analysis.denyText') : null, }), async () => { await this._setFileApproved(); @@ -417,7 +425,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, this.showImportRedactions = this._permissionsService.canImportRedactions(this.file); - const showReanalyse = (this.canReanalyse && this.file.excludedFromAutomaticAnalysis) || this.analysisForced; + const showReanalyse = this.canReanalyse || this.file.excludedFromAutomaticAnalysis || this.analysisForced; this.showReanalyseFilePreview = showReanalyse && this.isFilePreview; this.showReanalyseDossierOverview = showReanalyse && this.isDossierOverview; diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index b228549e1..64e622c8a 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -421,10 +421,22 @@ "warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!" }, "confirmation-dialog": { + "approve-file-without-analysis": { + "confirmationText": "", + "denyText": "", + "question": "", + "title": "" + }, "approve-file": { "question": "Dieses Dokument enthält ungesehene Änderungen. Möchten Sie es trotzdem genehmigen?", "title": "Warnung!" }, + "approve-multiple-files-without-analysis": { + "confirmationText": "", + "denyText": "", + "question": "", + "title": "" + }, "approve-multiple-files": { "question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen. Möchten Sie sie trotzdem genehmigen?", "title": "Warnung!" @@ -599,7 +611,6 @@ "placeholder": "Begründung" } }, - "disabled-auto-analysis": "", "document-info": { "save": "Dokumenteninformation speichern", "title": "Datei-Attribute anlegen" diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 701824728..1d0e5658b 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -470,10 +470,22 @@ "warning": "Warning: this cannot be undone!" }, "confirmation-dialog": { + "approve-file-without-analysis": { + "confirmationText": "Approve without analysis", + "denyText": "Cancel", + "question": "Analysis required to detect new redactions.", + "title": "Warning!" + }, "approve-file": { "question": "This document has unseen changes, do you wish to approve it anyway?", "title": "Warning!" }, + "approve-multiple-files-without-analysis": { + "confirmationText": "Approve without analysis", + "denyText": "Cancel", + "question": "Analysis required to detect new redactions for at least one file.", + "title": "Warning" + }, "approve-multiple-files": { "question": "At least one of the files you selected has unseen changes, do you wish to approve them anyway?", "title": "Warning!" @@ -655,7 +667,6 @@ "placeholder": "Reason" } }, - "disabled-auto-analysis": "Automatic analysys is disabled", "document-info": { "save": "Save Document Info", "title": "Introduce File Attributes" @@ -1248,9 +1259,12 @@ "jump-to-previous": "Jump to Previous", "label": "Workload", "page-is": "This page is", + "reset": "reset", "select": "Select", "select-all": "All", - "select-none": "None" + "select-none": "None", + "the-filters": "the filters", + "wrong-filters": "The selected filter combination is not possible. Please adjust or" }, "document-info": { "close": "Close Document Info", @@ -1306,6 +1320,7 @@ "filter-options": "Filter options", "filter-types": "Filter", "label": "Filter", + "pages-without-annotations": "Only pages without annotations", "redaction-changes": "Only annotations with redaction changes", "unseen-pages": "Only annotations on unseen pages", "with-comments": "Only annotations with comments" diff --git a/libs/common-ui b/libs/common-ui index e2f853655..f480a52cc 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit e2f85365512c68b927465ee5fe4c0d8e3d5dfe1a +Subproject commit f480a52cc384eea5ab54fb3b7bbc5db431c1506c diff --git a/libs/red-domain/src/lib/files/file.model.ts b/libs/red-domain/src/lib/files/file.model.ts index 2c4f19da3..b5930769c 100644 --- a/libs/red-domain/src/lib/files/file.model.ts +++ b/libs/red-domain/src/lib/files/file.model.ts @@ -107,7 +107,7 @@ export class File extends Entity implements IFile { this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW; this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW; this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL; - this.canBeApproved = !this.analysisRequired && !this.hasSuggestions && !this.isProcessing && !this.isError; + 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); diff --git a/package.json b/package.json index 5d6fca3d1..81e9aee99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redaction", - "version": "3.258.0", + "version": "3.261.0", "private": true, "license": "MIT", "scripts": { diff --git a/paligo-theme.tar.gz b/paligo-theme.tar.gz index 2f625b0cc..4541f9ed7 100644 Binary files a/paligo-theme.tar.gz and b/paligo-theme.tar.gz differ