From 1897264bcae62fbad95f8c81386bd9db98ac75fe Mon Sep 17 00:00:00 2001 From: Valentin Mihai Date: Sat, 22 Oct 2022 17:19:14 +0300 Subject: [PATCH] RED-3797 - Improve import redaction dialog --- .../page-exclusion.component.ts | 15 +----- .../import-redactions-dialog.html | 17 +++++- .../import-redactions-dialog.scss | 10 ++-- .../import-redactions-dialog.ts | 54 +++++++++++++++++-- .../file-actions/file-actions.component.ts | 3 +- .../files/redaction-import.service.ts | 6 ++- .../src/app/utils/page-ranges-mapper.ts | 22 ++++++++ apps/red-ui/src/assets/i18n/de.json | 11 +++- apps/red-ui/src/assets/i18n/en.json | 11 +++- libs/common-ui | 2 +- 10 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 apps/red-ui/src/app/utils/page-ranges-mapper.ts diff --git a/apps/red-ui/src/app/modules/file-preview/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/file-preview/components/page-exclusion/page-exclusion.component.ts index 1e2328ae9..370efa137 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/page-exclusion/page-exclusion.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/page-exclusion/page-exclusion.component.ts @@ -8,6 +8,7 @@ import { ExcludedPagesService } from '../../services/excluded-pages.service'; import { combineLatest, firstValueFrom, Observable } from 'rxjs'; import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { map, pluck } from 'rxjs/operators'; +import { extractPageRanges } from '@utils/page-ranges-mapper'; @Component({ selector: 'redaction-page-exclusion', @@ -52,21 +53,9 @@ export class PageExclusionComponent { async excludePagesRange(inputValue: string): Promise { this._loadingService.start(); - const value = inputValue.replace(/[^0-9-,]/g, ''); const file = this._state.file; try { - const pageRanges = value.split(',').map(range => { - const splitted = range.split('-'); - const startPage = parseInt(splitted[0], 10); - const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage; - if (!startPage || !endPage || startPage > file.numberOfPages || endPage > file.numberOfPages) { - throw new Error(); - } - return { - startPage, - endPage, - }; - }); + const pageRanges = extractPageRanges(inputValue, file); const excludePages$ = this._reanalysisService.excludePages({ pageRanges }, file.dossierId, file); await firstValueFrom(excludePages$); this._inputComponent.reset(); diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.html b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.html index a17b8e19f..813d6cc12 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.html +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.html @@ -4,10 +4,25 @@
+ +
+ + {{ 'import-redactions-dialog.import-only-for-pages' | translate }} + +
+ + +
+
-
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.scss b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.scss index 0f476c1e4..834fc609d 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.scss +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.scss @@ -1,7 +1,4 @@ .only-for-pages { - margin-top: 16px; - margin-left: 21px; - min-height: 34px; display: flex; flex-direction: row; align-items: center; @@ -10,4 +7,11 @@ width: fit-content; margin-right: 16px; } + + .iqser-input-group { + width: 260px !important; + max-width: 260px !important; + margin-top: 25px; + gap: 10px; + } } diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.ts index ec90b2ce7..c4546adec 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/import-redactions-dialog/import-redactions-dialog.ts @@ -5,6 +5,8 @@ import { firstValueFrom } from 'rxjs'; import { RedactionImportService } from '@services/files/redaction-import.service'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpStatusCode } from '@angular/common/http'; +import { extractPageRanges } from '@utils/page-ranges-mapper'; +import { File } from '@red/domain'; interface ImportData { dossierId: string; @@ -18,8 +20,10 @@ interface ImportData { export class ImportRedactionsDialogComponent extends BaseDialogComponent { @ViewChild('attachFileInput', { static: true }) attachFileInput: ElementRef; - fileToImport: File | null; + fileToImport: Blob | null; + numberOfPages = 0; onlyForSpecificPages = false; + rangesValue = ''; constructor( protected readonly _dialogRef: MatDialogRef, @@ -29,16 +33,60 @@ export class ImportRedactionsDialogComponent extends BaseDialogComponent { super(_dialogRef); } - fileChanged(file: File | null) { + fileChanged(file: Blob | null) { this.fileToImport = file; if (!this.fileToImport) { this.onlyForSpecificPages = false; + this.rangesValue = ''; + return; } + this.#extractNumberOfPages(this.fileToImport); + } + + #extractNumberOfPages(file: Blob) { + const reader = new FileReader(); + reader.readAsBinaryString(file); + reader.onloadend = () => { + this.numberOfPages = (reader.result as any).match(/\/Type[\s]*\/Page[^s]/g).length; + }; + } + + get #pagesToImport(): Set { + const pageRanges = extractPageRanges(this.rangesValue, { numberOfPages: this.numberOfPages } as File); + const pagesToImport = new Set(); + pageRanges.forEach(r => { + for (let i = r.startPage; i <= r.endPage; i++) { + pagesToImport.add(i); + } + }); + + return pagesToImport; + } + + get disabled(): boolean { + if (!this.fileToImport) { + return true; + } + if (this.onlyForSpecificPages) { + try { + extractPageRanges(this.rangesValue, { numberOfPages: this.numberOfPages } as File); + } catch (e) { + return true; + } + } + return false; } async save(): Promise { this._loadingService.start(); - const import$ = this._redactionImportService.importRedactions(this.data.dossierId, this.data.fileId, this.fileToImport); + + const pagesToImport = this.onlyForSpecificPages ? this.#pagesToImport : null; + const import$ = this._redactionImportService.importRedactions( + this.data.dossierId, + this.data.fileId, + this.fileToImport, + pagesToImport, + ); const result: any = await firstValueFrom(import$).catch(error => this._toaster.error(_('import-redactions-dialog.http.error'), { error }), ); diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts index 20c9e260e..9993c1a47 100644 --- a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts @@ -309,8 +309,7 @@ export class FileActionsComponent implements OnChanges { } private _openImportRedactionsDialog($event: MouseEvent) { - $event.stopPropagation(); - this._dialogService.openDialog('importRedactions', null, { dossierId: this.file.dossierId, fileId: this.file.fileId }); + this._dialogService.openDialog('importRedactions', $event, { dossierId: this.file.dossierId, fileId: this.file.fileId }); } private _openDeleteFileDialog($event: MouseEvent) { diff --git a/apps/red-ui/src/app/services/files/redaction-import.service.ts b/apps/red-ui/src/app/services/files/redaction-import.service.ts index 18cf4e159..d3f28a8f3 100644 --- a/apps/red-ui/src/app/services/files/redaction-import.service.ts +++ b/apps/red-ui/src/app/services/files/redaction-import.service.ts @@ -6,13 +6,17 @@ export class RedactionImportService extends GenericService { protected readonly _defaultModelPath = 'import-redactions'; @Validate() - importRedactions(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, file?: Blob) { + importRedactions(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, file: Blob, pagesToImport: Set | null) { const formParams = new FormData(); if (file !== undefined) { formParams.append('file', file); } + if (pagesToImport) { + formParams.append('pageInclusionRequest', Array.from(pagesToImport).toString()); + } + const headers = HeadersConfiguration.getHeaders({ contentType: false }).append('ngsw-bypass', 'true'); return this._http.post(`/${this._defaultModelPath}/${dossierId}/${fileId}`, formParams, { diff --git a/apps/red-ui/src/app/utils/page-ranges-mapper.ts b/apps/red-ui/src/app/utils/page-ranges-mapper.ts new file mode 100644 index 000000000..e6ef22e67 --- /dev/null +++ b/apps/red-ui/src/app/utils/page-ranges-mapper.ts @@ -0,0 +1,22 @@ +import { File } from '@red/domain'; + +interface PageRange { + startPage: number; + endPage: number; +} + +export function extractPageRanges(inputValue: string, file: File): PageRange[] { + const value = inputValue.replace(/[^0-9-,]/g, ''); + return value.split(',').map(range => { + const splitted = range.split('-'); + const startPage = parseInt(splitted[0], 10); + const endPage = splitted.length > 1 ? parseInt(splitted[1], 10) : startPage; + if (!startPage || !endPage || startPage > file.numberOfPages || endPage > file.numberOfPages) { + throw new Error(); + } + return { + startPage, + endPage, + }; + }); +} diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index 7b343da8b..f97be4b2f 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -1557,8 +1557,12 @@ "error": "", "success": "" }, - "title": "", - "upload-area-text": "" + "import-only-for-pages": "", + "range": { + "label": "", + "placeholder": "" + }, + "title": "" }, "initials-avatar": { "unassigned": "Unbekannt", @@ -2053,6 +2057,9 @@ "question": "Wählen Sie, wie Sie fortfahren möchten:", "title": "Das Wörterbuch hat bereits Einträge!" }, + "upload-file": { + "upload-area-text": "" + }, "upload-status": { "dialog": { "actions": { diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 5efc1f893..8304184e0 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -1557,8 +1557,12 @@ "error": "Failed to import redactions! {error}", "success": "Redactions have been imported!" }, - "title": "Import document with redactions", - "upload-area-text": "Click or drag & drop anywhere on this area..." + "import-only-for-pages": "Import only for pages", + "range": { + "label": "Minus(-) for range and comma(,) for enumeration.", + "placeholder": "e.g. 1-20,22,32" + }, + "title": "Import document with redactions" }, "initials-avatar": { "unassigned": "Unassigned", @@ -2053,6 +2057,9 @@ "question": "Choose how you want to proceed:", "title": "The dictionary already has entries!" }, + "upload-file": { + "upload-area-text": "Click or drag & drop anywhere on this area..." + }, "upload-status": { "dialog": { "actions": { diff --git a/libs/common-ui b/libs/common-ui index 401e6a047..76289b16e 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 401e6a047f6af09e9b737e80df6f86fd18f009d3 +Subproject commit 76289b16e002506c80f83697f33ae3118b30ff1a