From 64ed8265be892398a3bb12968bb30d41d26b9a9e Mon Sep 17 00:00:00 2001 From: Timo Date: Tue, 15 Dec 2020 13:32:55 +0200 Subject: [PATCH] UI fixes and report download in 3/4 cases --- apps/red-ui/src/app/app.module.ts | 6 +- .../file-actions/file-actions.component.html | 14 ++-- .../file-actions/file-actions.component.ts | 1 + .../circle-button.component.scss | 6 ++ .../report-download-btn.component.html | 20 +++++ .../report-download-btn.component.scss | 0 .../report-download-btn.component.ts | 52 ++++++++++++ .../add-edit-project-dialog.component.html | 4 +- .../manual-annotation-dialog.component.html | 2 +- apps/red-ui/src/app/icons/icons.module.ts | 1 + .../screens/file/model/annotation.wrapper.ts | 4 + .../app/screens/file/model/file-data.model.ts | 4 + .../project-listing-actions.component.html | 48 +++++++++++ .../project-listing-actions.component.scss | 0 .../project-listing-actions.component.ts | 82 +++++++++++++++++++ .../project-listing-screen.component.html | 57 +------------ .../project-listing-screen.component.ts | 58 +------------ .../project-details.component.html | 4 + .../project-overview-screen.component.html | 11 +-- .../project-overview-screen.component.ts | 5 -- .../red-ui/src/app/state/app-state.service.ts | 9 +- apps/red-ui/src/assets/i18n/en.json | 11 ++- .../general/pdftron-action-false-positive.svg | 32 ++++++++ .../src/assets/icons/general/template.svg | 7 ++ apps/red-ui/src/assets/styles/red-tables.scss | 6 ++ 25 files changed, 305 insertions(+), 139 deletions(-) create mode 100644 apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.html create mode 100644 apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.scss create mode 100644 apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.ts create mode 100644 apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.html create mode 100644 apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.scss create mode 100644 apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.ts create mode 100644 apps/red-ui/src/assets/icons/general/pdftron-action-false-positive.svg create mode 100644 apps/red-ui/src/assets/icons/general/template.svg diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index caccfd437..fa74b8195 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -95,6 +95,8 @@ import { RulesScreenComponent } from './screens/admin/rules-screen/rules-screen. import { WatermarkScreenComponent } from './screens/admin/watermark-screen/watermark-screen.component'; import { PdfViewerScreenComponent } from './screens/pdf-viewer-screen/pdf-viewer-screen.component'; import { HtmlDebugScreenComponent } from './screens/html-debug-screen/html-debug-screen.component'; +import { ReportDownloadBtnComponent } from './components/buttons/report-download-btn/report-download-btn.component'; +import { ProjectListingActionsComponent } from './screens/project-listing-screen/project-listing-actions/project-listing-actions.component'; export function HttpLoaderFactory(httpClient: HttpClient) { return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json'); @@ -287,7 +289,9 @@ const matImports = [ RulesScreenComponent, WatermarkScreenComponent, PdfViewerScreenComponent, - HtmlDebugScreenComponent + HtmlDebugScreenComponent, + ReportDownloadBtnComponent, + ProjectListingActionsComponent ], imports: [ BrowserModule, diff --git a/apps/red-ui/src/app/common/file-actions/file-actions.component.html b/apps/red-ui/src/app/common/file-actions/file-actions.component.html index eb2505a8b..4e5039e9e 100644 --- a/apps/red-ui/src/app/common/file-actions/file-actions.component.html +++ b/apps/red-ui/src/app/common/file-actions/file-actions.component.html @@ -1,4 +1,4 @@ -
+
- - + (); + actionMenuOpen: boolean; screen: 'file-preview' | 'project-overview'; diff --git a/apps/red-ui/src/app/components/buttons/circle-button/circle-button.component.scss b/apps/red-ui/src/app/components/buttons/circle-button/circle-button.component.scss index d63b4b056..46a9a7873 100644 --- a/apps/red-ui/src/app/components/buttons/circle-button/circle-button.component.scss +++ b/apps/red-ui/src/app/components/buttons/circle-button/circle-button.component.scss @@ -1,5 +1,10 @@ @import '../../../../assets/styles/red-variables'; +:host { + height: 34px; + width: 34px; +} + button { height: 34px; width: 34px; @@ -24,6 +29,7 @@ button { &.warn { background-color: $yellow-2; + &:hover { background-color: $yellow-2; } diff --git a/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.html b/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.html new file mode 100644 index 000000000..3e4c2e7ea --- /dev/null +++ b/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.html @@ -0,0 +1,20 @@ + + + +
+
+
+
+
+
+
diff --git a/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.scss b/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.ts b/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.ts new file mode 100644 index 000000000..b9ccedd79 --- /dev/null +++ b/apps/red-ui/src/app/components/buttons/report-download-btn/report-download-btn.component.ts @@ -0,0 +1,52 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { PermissionsService } from '../../../common/service/permissions.service'; +import { AppStateService } from '../../../state/app-state.service'; +import { ProjectWrapper } from '../../../state/model/project.wrapper'; +import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper'; + +export type MenuState = 'OPEN' | 'CLOSED'; + +@Component({ + selector: 'redaction-report-download-btn', + templateUrl: './report-download-btn.component.html', + styleUrls: ['./report-download-btn.component.scss'] +}) +export class ReportDownloadBtnComponent implements OnInit { + @Input() project: ProjectWrapper; + @Input() file: FileStatusWrapper; + @Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above'; + @Input() type: 'default' | 'primary' | 'warn' | 'dark-bg' = 'default'; + @Input() tooltipClass: string; + + @Output() menuStateChanged = new EventEmitter(); + + constructor(public readonly permissionsService: PermissionsService, private readonly _appStateService: AppStateService) {} + + ngOnInit(): void {} + + openReportMenu($event: MouseEvent) { + $event.stopPropagation(); + this.menuStateChanged.emit('OPEN'); + } + + onMenuClosed() { + this.menuStateChanged.emit('CLOSED'); + } + + get isApproved() { + if (this.file) { + return this.file.isApproved; + } else { + return this.project.allFilesApproved; + } + } + + downloadRedactionReport($event: MouseEvent, template: string) { + $event.preventDefault(); + if (this.file) { + this._appStateService.downloadFileRedactionReport(this.file, template); + } else { + this._appStateService.downloadRedactionReport(this.project, template); + } + } +} diff --git a/apps/red-ui/src/app/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html b/apps/red-ui/src/app/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html index b99461ad6..fd0f49496 100644 --- a/apps/red-ui/src/app/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html +++ b/apps/red-ui/src/app/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.html @@ -16,10 +16,10 @@ />
-
+
{{ 'project-listing.add-edit-dialog.form.template' | translate }} - + {{ type }} diff --git a/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.html b/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.html index dc4b4c8f8..93b2e0114 100644 --- a/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.html +++ b/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.html @@ -18,7 +18,7 @@
- + {{ option.label }} diff --git a/apps/red-ui/src/app/icons/icons.module.ts b/apps/red-ui/src/app/icons/icons.module.ts index bfc4b3585..c77fc9980 100644 --- a/apps/red-ui/src/app/icons/icons.module.ts +++ b/apps/red-ui/src/app/icons/icons.module.ts @@ -59,6 +59,7 @@ export class IconsModule { 'sort-desc', 'status', 'trash', + 'template', 'user', 'check-alt', 'page', diff --git a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts index 068970687..e5f375c0a 100644 --- a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts +++ b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts @@ -38,6 +38,10 @@ export class AnnotationWrapper { return this.superType === 'ignore'; } + get isFalsePositive() { + return this.dictionary === 'false_positive'; + } + get isManual() { return this.superType === 'manual'; } diff --git a/apps/red-ui/src/app/screens/file/model/file-data.model.ts b/apps/red-ui/src/app/screens/file/model/file-data.model.ts index 9bb714b7a..c27f5f27b 100644 --- a/apps/red-ui/src/app/screens/file/model/file-data.model.ts +++ b/apps/red-ui/src/app/screens/file/model/file-data.model.ts @@ -45,6 +45,10 @@ export class FileDataModel { return; } + if (annotation.isFalsePositive && !areDevFeaturesEnabled) { + return; + } + if (annotation.isReadyForAnalysis && annotation.isApproved) { // } else { diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.html b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.html new file mode 100644 index 000000000..df8e4448c --- /dev/null +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.html @@ -0,0 +1,48 @@ + +
+ + + + + + + + + + + + + + + +
diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.scss b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.ts b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.ts new file mode 100644 index 000000000..a5010c741 --- /dev/null +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-actions/project-listing-actions.component.ts @@ -0,0 +1,82 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { PermissionsService } from '../../../common/service/permissions.service'; +import { ProjectWrapper } from '../../../state/model/project.wrapper'; +import { StatusSorter } from '../../../common/sorters/status-sorter'; +import { download } from '../../../utils/file-download-utils'; +import { computerize } from '../../../utils/functions'; +import { FileManagementControllerService } from '@redaction/red-ui-http'; +import { AppStateService } from '../../../state/app-state.service'; +import { DialogService } from '../../../dialogs/dialog.service'; + +@Component({ + selector: 'redaction-project-listing-actions', + templateUrl: './project-listing-actions.component.html', + styleUrls: ['./project-listing-actions.component.scss'] +}) +export class ProjectListingActionsComponent implements OnInit { + @Input() project: ProjectWrapper; + @Output() actionPerformed = new EventEmitter(); + actionMenuOpen: boolean = false; + + constructor( + public readonly permissionsService: PermissionsService, + public readonly appStateService: AppStateService, + private readonly _dialogService: DialogService, + private readonly _fileManagementControllerService: FileManagementControllerService + ) {} + + ngOnInit(): void {} + + public openAssignProjectOwnerDialog($event: MouseEvent, project: ProjectWrapper) { + this._dialogService.openAssignProjectMembersAndOwnerDialog($event, project); + } + + public openDeleteProjectDialog($event: MouseEvent, project: ProjectWrapper) { + this._dialogService.openDeleteProjectDialog($event, project, () => { + this.actionPerformed.emit(); + }); + } + + openEditProjectDialog($event: MouseEvent, project: ProjectWrapper) { + this._dialogService.openEditProjectDialog($event, project, () => { + this.actionPerformed.emit(); + }); + } + + async reanalyseProject($event: MouseEvent, project: ProjectWrapper) { + $event.stopPropagation(); + await this.appStateService.reanalyzeProject(project); + await this.appStateService.loadAllProjects(); + this.actionPerformed.emit(); + } + + // Download Files + public downloadRedactedFiles($event: MouseEvent, project: ProjectWrapper) { + $event.stopPropagation(); + this._fileManagementControllerService + .downloadRedactedFiles({ fileIds: project.files.map((file) => file.fileId) }, project.projectId, false, 'response') + .subscribe((data) => { + download(data, 'redacted_files_' + computerize(project.name) + '.zip'); + }); + } + + public canDownloadRedactedFiles(project: ProjectWrapper) { + return project.files.reduce((acc, file) => acc && this.permissionsService.canDownloadRedactedFile(file), true); + } + + public getProjectStatusConfig(pw: ProjectWrapper) { + const obj = pw.files.reduce((acc, file) => { + const status = file.status; + if (!acc[status]) { + acc[status] = 1; + } else { + acc[status]++; + } + return acc; + }, {}); + + return Object.keys(obj) + .sort((a, b) => StatusSorter[a] - StatusSorter[b]) + .map((status) => ({ length: obj[status], color: status })); + } +} diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html index ca927b0bd..c22f5dbac 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.html @@ -105,62 +105,7 @@
- -
- - - - - - - - - - - - - - - - -
+
diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts index 6a6625700..ebc62af5c 100644 --- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts +++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts @@ -3,7 +3,7 @@ import { FileManagementControllerService, Project } from '@redaction/red-ui-http import { AppStateService } from '../../state/app-state.service'; import { UserService } from '../../user/user.service'; import { DoughnutChartConfig } from '../../components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { computerize, groupBy, humanize } from '../../utils/functions'; +import { groupBy, humanize } from '../../utils/functions'; import { DialogService } from '../../dialogs/dialog.service'; import { FilterModel } from '../../common/filter/model/filter.model'; import { @@ -25,7 +25,6 @@ import { StatusSorter } from '../../common/sorters/status-sorter'; import { Router } from '@angular/router'; import { FormBuilder, FormGroup } from '@angular/forms'; import { debounce } from '../../utils/debounce'; -import { download } from '../../utils/file-download-utils'; @Component({ selector: 'redaction-project-listing-screen', @@ -37,6 +36,7 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { public documentsChartData: DoughnutChartConfig[] = []; public searchForm: FormGroup; + public actionMenuOpen: boolean; public statusFilters: FilterModel[]; public peopleFilters: FilterModel[]; @@ -153,44 +153,10 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { }); } - public openDeleteProjectDialog($event: MouseEvent, project: ProjectWrapper) { - this._dialogService.openDeleteProjectDialog($event, project, () => { - this._calculateData(); - }); - } - - public downloadRedactionReport($event: MouseEvent, project: ProjectWrapper) { - $event.stopPropagation(); - this.appStateService.downloadRedactionReport(project); - } - public openAssignProjectOwnerDialog($event: MouseEvent, project: ProjectWrapper) { this._dialogService.openAssignProjectMembersAndOwnerDialog($event, project); } - public getProjectStatusConfig(pw: ProjectWrapper) { - const obj = pw.files.reduce((acc, file) => { - const status = file.status; - if (!acc[status]) { - acc[status] = 1; - } else { - acc[status]++; - } - return acc; - }, {}); - - return Object.keys(obj) - .sort((a, b) => StatusSorter[a] - StatusSorter[b]) - .map((status) => ({ length: obj[status], color: status })); - } - - async reanalyseProject($event: MouseEvent, project: ProjectWrapper) { - $event.stopPropagation(); - await this.appStateService.reanalyzeProject(project); - await this.appStateService.loadAllProjects(); - this._calculateData(); - } - private _computeAllFilters() { const allDistinctFileStatus = new Set(); const allDistinctPeople = new Set(); @@ -284,23 +250,7 @@ export class ProjectListingScreenComponent implements OnInit, OnDestroy { this.displayedProjects = this._filteredProjects.filter((project) => project.name.toLowerCase().includes(value.query.toLowerCase())); } - openEditProjectDialog($event: MouseEvent, project: ProjectWrapper) { - this._dialogService.openEditProjectDialog($event, project, () => { - this._calculateData(); - }); - } - - // Download Files - public downloadRedactedFiles($event: MouseEvent, project: ProjectWrapper) { - $event.stopPropagation(); - this._fileManagementControllerService - .downloadRedactedFiles({ fileIds: project.files.map((file) => file.fileId) }, project.projectId, false, 'response') - .subscribe((data) => { - download(data, 'redacted_files_' + computerize(project.name) + '.zip'); - }); - } - - public canDownloadRedactedFiles(project: ProjectWrapper) { - return project.files.reduce((acc, file) => acc && this.permissionsService.canDownloadRedactedFile(file), true); + actionPerformed(pw: ProjectWrapper) { + this._calculateData(); } } diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html index 0871ef7fd..9d1d92815 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.html @@ -80,6 +80,10 @@ 'project-overview.project-details.stats.due-date' | translate: { date: appStateService.activeProject.project.dueDate | date: 'd MMM. yyyy' } }}
+
+ + {{ 'EFSA 1 (Vertebrate Authors)' }} +
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html index 93ca31757..ad724dac4 100644 --- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html +++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html @@ -57,14 +57,9 @@ tooltipPosition="below" icon="red:assign" > - + + + { this.reloadProjects(); diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index e690beaae..5a29fd507 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -267,7 +267,7 @@ export class AppStateService { await this._reanalysisControllerService.reanalyzeProject(project.projectId).toPromise(); } - downloadRedactionReport(project?: ProjectWrapper) { + downloadRedactionReport(project?: ProjectWrapper, template?: string) { if (!project) { project = this.activeProject; } @@ -339,7 +339,9 @@ export class AppStateService { return foundProject.project; } catch (error) { this._notificationService.showToastNotification( - this._translateService.instant('projects.add-edit-dialog.errors.save'), + this._translateService.instant( + error.status === 409 ? 'projects.add-edit-dialog.errors.project-already-exists' : 'projects.add-edit-dialog.errors.generic' + ), null, NotificationType.ERROR ); @@ -384,7 +386,7 @@ export class AppStateService { } } - downloadFileRedactionReport(file?: FileStatusWrapper) { + downloadFileRedactionReport(file?: FileStatusWrapper, template?: string) { if (!file) { file = this.activeFile; } @@ -407,6 +409,7 @@ export class AppStateService { tap((typesResponse) => { for (const type of typesResponse.types) { this._dictionaryData[type.type] = type; + this._dictionaryData[type.type].virtual = type.type === 'false_positive'; } }) ); diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 33d1e5183..454b3a304 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -67,7 +67,11 @@ "report": { "unavailable": "Redaction report is only available once all files have been approved.", "unavailable-single": "Redaction report is only available once this file has been approved.", - "action": "Download Redaction Report" + "action": { + "label": "Download Redaction Report", + "efsa": "Download with EFSA Template", + "syngenta": "Download with Syngenta Template" + } }, "project-listing": { "search": "Project name...", @@ -114,6 +118,10 @@ "due-date": "Due Date", "template": "Project Template" }, + "errors": { + "project-already-exists": "Project with this name already exists!", + "generic": "Failed to save project" + }, "actions": { "save": "Save", "save-and-add-members": "Save and Edit Team" @@ -469,6 +477,7 @@ "rectangle": "Custom Rectangle", "dictionary": "Dictionary", "reason": "Reason", + "reason-placeholder": "Select a reason ...", "legalBasis": "Legal Basis", "comment": "Comment" } diff --git a/apps/red-ui/src/assets/icons/general/pdftron-action-false-positive.svg b/apps/red-ui/src/assets/icons/general/pdftron-action-false-positive.svg new file mode 100644 index 000000000..1de523d7d --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/pdftron-action-false-positive.svg @@ -0,0 +1,32 @@ + + + + + diff --git a/apps/red-ui/src/assets/icons/general/template.svg b/apps/red-ui/src/assets/icons/general/template.svg new file mode 100644 index 000000000..da96bbb7c --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/template.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/apps/red-ui/src/assets/styles/red-tables.scss b/apps/red-ui/src/assets/styles/red-tables.scss index 33076b503..52a5f9df0 100644 --- a/apps/red-ui/src/assets/styles/red-tables.scss +++ b/apps/red-ui/src/assets/styles/red-tables.scss @@ -81,6 +81,12 @@ mat-icon { width: 14px; } + + &.active { + display: flex; + // compensate for scroll + padding-right: 23px; + } } &:hover {