From 7b31e4c59ca99a3dcf4b60119d67c6f18f1a9107 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Mon, 10 May 2021 09:04:34 +0300 Subject: [PATCH] update paths & refactor imports --- apps/red-ui/src/app/app-routing.module.ts | 14 +- apps/red-ui/src/app/app.component.ts | 4 +- apps/red-ui/src/app/app.module.ts | 26 +- .../auth-error/auth-error.component.ts | 4 +- .../base-screen/base-screen.component.ts | 28 +- .../downloads-list-screen.component.ts | 12 +- .../notifications.component.html | 10 +- .../notifications/notifications.component.ts | 30 +- .../user-profile-screen.component.ts | 14 +- .../src/app/guards/composite-route.guard.ts | 2 +- .../app/models/file/annotation.permissions.ts | 22 +- .../src/app/models/file/file-data.model.ts | 2 +- .../app/models/file/file-status.wrapper.ts | 2 +- .../file/redaction-log-entry.wrapper.ts | 2 +- .../app/modules/admin/admin-routing.module.ts | 6 +- .../src/app/modules/admin/admin.module.ts | 2 +- .../admin-breadcrumbs.component.ts | 6 +- .../combo-chart/combo-chart.component.scss | 1 + .../combo-chart/combo-chart.component.ts | 4 +- .../rule-set-actions.component.ts | 4 +- .../components/side-nav/side-nav.component.ts | 6 +- .../users-stats/users-stats.component.ts | 2 +- .../add-edit-dictionary-dialog.component.ts | 4 +- ...-edit-file-attribute-dialog.component.html | 10 +- ...dd-edit-file-attribute-dialog.component.ts | 2 +- .../add-edit-rule-set-dialog.component.ts | 48 +-- .../add-edit-user-dialog.component.ts | 42 +-- ...-delete-file-attribute-dialog.component.ts | 10 +- .../confirm-delete-users-dialog.component.ts | 18 +- .../edit-color-dialog.component.ts | 4 +- .../active-fields-listing.component.ts | 2 +- ...-attributes-csv-import-dialog.component.ts | 60 ++-- .../smtp-auth-dialog.component.ts | 2 +- .../screens/audit/audit-screen.component.ts | 26 +- .../default-colors-screen.component.ts | 23 +- .../dictionary-listing-screen.component.ts | 47 ++- .../dictionary-overview-screen.component.ts | 10 +- .../digital-signature-screen.component.ts | 18 +- ...ile-attributes-listing-screen.component.ts | 38 +- .../license-information-screen.component.ts | 69 ++-- .../rule-sets-listing-screen.component.ts | 24 +- .../screens/rules/rules-screen.component.ts | 36 +- .../smtp-config-screen.component.ts | 31 +- .../user-listing-screen.component.ts | 67 ++-- .../watermark/watermark-screen.component.ts | 90 +++-- .../admin/services/admin-dialog.service.ts | 6 +- .../red-ui/src/app/modules/auth/auth.guard.ts | 6 +- .../src/app/modules/auth/auth.module.ts | 7 +- .../src/app/modules/auth/red-role.guard.ts | 4 +- .../annotation-actions.component.ts | 16 +- .../annotation-remove-actions.component.ts | 8 +- ...project-overview-bulk-actions.component.ts | 60 ++-- .../components/comments/comments.component.ts | 8 +- .../document-info/document-info.component.ts | 10 +- .../file-actions/file-actions.component.ts | 38 +- .../file-workload/file-workload.component.ts | 53 ++- .../needs-work-badge.component.ts | 24 +- .../page-indicator.component.ts | 62 ++-- .../pdf-viewer/pdf-viewer.component.ts | 109 +++--- .../project-details.component.ts | 30 +- .../project-listing-actions.component.ts | 12 +- .../project-listing-details.component.ts | 6 +- .../team-members/team-members.component.ts | 10 +- .../type-annotation-icon.component.ts | 4 +- .../type-filter/type-filter.component.ts | 11 +- .../add-edit-project-dialog.component.ts | 48 +-- .../assign-owner-dialog.component.ts | 162 ++++----- .../document-info-dialog.component.ts | 12 +- .../force-redaction-dialog.component.ts | 8 +- .../manual-annotation-dialog.component.ts | 36 +- .../remove-annotations-dialog.component.ts | 2 +- .../projects/projects-routing.module.ts | 4 +- .../app/modules/projects/projects.module.ts | 4 +- .../file-preview-screen.component.ts | 35 +- .../project-listing-screen.component.ts | 176 ++++----- .../project-overview-screen.component.ts | 167 +++++---- .../services/annotation-actions.service.ts | 30 +- .../services/annotation-draw.service.ts | 40 +-- .../services/annotation-processing.service.ts | 62 ++-- .../projects/services/file-action.service.ts | 42 +-- .../services/manual-annotation.service.ts | 54 +-- .../services/pdf-viewer-data.service.ts | 8 +- .../services/projects-dialog.service.ts | 12 +- .../shared/base/base-listing.component.ts | 186 +++++----- .../annotation-icon.component.ts | 8 +- .../file-download-btn.component.ts | 10 +- .../icon-button/icon-button.component.ts | 2 +- .../user-button/user-button.component.ts | 2 +- .../dictionary-annotation-icon.component.ts | 2 +- .../components/filter/filter.component.ts | 48 +-- .../components/filter/utils/filter-utils.ts | 6 +- .../hidden-action/hidden-action.component.ts | 5 +- .../initials-avatar.component.ts | 23 +- .../pagination/pagination.component.ts | 43 ++- .../search-input/search-input.component.ts | 8 +- .../simple-doughnut-chart.component.ts | 24 +- .../status-bar/status-bar.component.ts | 2 +- .../table-col-name.component.ts | 2 +- .../directives/has-scrollbar.directive.ts | 10 +- ...navigate-last-projects-screen.directive.ts | 2 +- .../shared/directives/sync-width.directive.ts | 2 +- .../app/modules/shared/pipes/humanize.pipe.ts | 2 +- .../file-drop/file-drop.component.ts | 4 +- .../file-upload-download.module.ts | 2 +- .../services/file-download.service.ts | 10 +- .../services/file-drop-overlay.service.ts | 14 +- .../services/file-upload.service.ts | 58 +-- .../services/status-overlay.service.ts | 14 +- .../src/app/services/permissions.service.ts | 6 +- .../app/services/translate-chart.service.ts | 7 +- apps/red-ui/src/app/services/user.service.ts | 43 +-- apps/red-ui/src/app/state/app-state.guard.ts | 2 +- .../red-ui/src/app/state/app-state.service.ts | 339 +++++++++--------- .../src/app/state/model/project.wrapper.ts | 30 +- .../src/app/utils/api-path-interceptor.ts | 2 +- apps/red-ui/src/app/utils/file-drop-utils.ts | 4 +- apps/red-ui/src/app/utils/uuid-helper.ts | 1 + tsconfig.base.json | 13 +- 118 files changed, 1617 insertions(+), 1589 deletions(-) diff --git a/apps/red-ui/src/app/app-routing.module.ts b/apps/red-ui/src/app/app-routing.module.ts index 4ccfaae0d..737cf6a86 100644 --- a/apps/red-ui/src/app/app-routing.module.ts +++ b/apps/red-ui/src/app/app-routing.module.ts @@ -1,14 +1,14 @@ -import { AuthErrorComponent } from './components/auth-error/auth-error.component'; +import { AuthErrorComponent } from '@components/auth-error/auth-error.component'; import { AuthGuard } from './modules/auth/auth.guard'; -import { CompositeRouteGuard } from './guards/composite-route.guard'; +import { CompositeRouteGuard } from '@guards/composite-route.guard'; import { RedRoleGuard } from './modules/auth/red-role.guard'; -import { BaseScreenComponent } from './components/base-screen/base-screen.component'; +import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; import { RouteReuseStrategy, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import { DownloadsListScreenComponent } from './components/downloads-list-screen/downloads-list-screen.component'; -import { AppStateGuard } from './state/app-state.guard'; -import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component'; -import { CustomRouteReuseStrategy } from './utils/custom-route-reuse.strategy'; +import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component'; +import { AppStateGuard } from '@state/app-state.guard'; +import { UserProfileScreenComponent } from '@components/user-profile/user-profile-screen.component'; +import { CustomRouteReuseStrategy } from '@utils/custom-route-reuse.strategy'; const routes = [ { diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index d8a314e3b..d916c2840 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; -import { AppLoadStateService } from './services/app-load-state.service'; -import { RouterHistoryService } from './services/router-history.service'; +import { AppLoadStateService } from '@services/app-load-state.service'; +import { RouterHistoryService } from '@services/router-history.service'; @Component({ selector: 'redaction-root', diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index ab67d4208..7313a7497 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -4,28 +4,28 @@ import { AppComponent } from './app.component'; import { ActivatedRoute, Router } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; -import { BaseScreenComponent } from './components/base-screen/base-screen.component'; +import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; import { ApiModule } from '@redaction/red-ui-http'; -import { ApiPathInterceptor } from './utils/api-path-interceptor'; +import { ApiPathInterceptor } from '@utils/api-path-interceptor'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { languageInitializer } from './i18n/language.initializer'; -import { LanguageService } from './i18n/language.service'; +import { languageInitializer } from '@i18n/language.initializer'; +import { LanguageService } from '@i18n/language.service'; import { ToastrModule } from 'ngx-toastr'; import { ServiceWorkerModule } from '@angular/service-worker'; -import { environment } from '../environments/environment'; +import { environment } from '@environments/environment'; import { AuthModule } from './modules/auth/auth.module'; -import { LogoComponent } from './components/logo/logo.component'; -import { AuthErrorComponent } from './components/auth-error/auth-error.component'; -import { ToastComponent } from './components/toast/toast.component'; +import { LogoComponent } from '@components/logo/logo.component'; +import { AuthErrorComponent } from '@components/auth-error/auth-error.component'; +import { ToastComponent } from '@components/toast/toast.component'; import { HttpCacheInterceptor } from '@redaction/red-cache'; -import { NotificationsComponent } from './components/notifications/notifications.component'; +import { NotificationsComponent } from '@components/notifications/notifications.component'; import { KeycloakService } from 'keycloak-angular'; -import { DownloadsListScreenComponent } from './components/downloads-list-screen/downloads-list-screen.component'; +import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component'; import { AppRoutingModule } from './app-routing.module'; -import { SharedModule } from './modules/shared/shared.module'; -import { FileUploadDownloadModule } from './modules/upload-download/file-upload-download.module'; -import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component'; +import { SharedModule } from '@shared/shared.module'; +import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module'; +import { UserProfileScreenComponent } from '@components/user-profile/user-profile-screen.component'; import { PlatformLocation } from '@angular/common'; import { BASE_HREF } from './tokens'; diff --git a/apps/red-ui/src/app/components/auth-error/auth-error.component.ts b/apps/red-ui/src/app/components/auth-error/auth-error.component.ts index b6516e085..351af3380 100644 --- a/apps/red-ui/src/app/components/auth-error/auth-error.component.ts +++ b/apps/red-ui/src/app/components/auth-error/auth-error.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { UserService } from '../../services/user.service'; -import { AppConfigKey, AppConfigService } from '../../modules/app-config/app-config.service'; +import { UserService } from '@services/user.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; @Component({ selector: 'redaction-auth-error', diff --git a/apps/red-ui/src/app/components/base-screen/base-screen.component.ts b/apps/red-ui/src/app/components/base-screen/base-screen.component.ts index 37c41d960..444362088 100644 --- a/apps/red-ui/src/app/components/base-screen/base-screen.component.ts +++ b/apps/red-ui/src/app/components/base-screen/base-screen.component.ts @@ -1,14 +1,14 @@ import { Component } from '@angular/core'; -import { UserService } from '../../services/user.service'; -import { AppStateService } from '../../state/app-state.service'; -import { LanguageService } from '../../i18n/language.service'; -import { PermissionsService } from '../../services/permissions.service'; -import { UserPreferenceService } from '../../services/user-preference.service'; +import { UserService } from '@services/user.service'; +import { AppStateService } from '@state/app-state.service'; +import { LanguageService } from '@i18n/language.service'; +import { PermissionsService } from '@services/permissions.service'; +import { UserPreferenceService } from '@services/user-preference.service'; import { Router } from '@angular/router'; -import { AppConfigService } from '../../modules/app-config/app-config.service'; +import { AppConfigService } from '@app-config/app-config.service'; import { Title } from '@angular/platform-browser'; -import { FileDownloadService } from '../../modules/upload-download/services/file-download.service'; -import { StatusOverlayService } from '../../modules/upload-download/services/status-overlay.service'; +import { FileDownloadService } from '@upload-download/services/file-download.service'; +import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; import { TranslateService } from '@ngx-translate/core'; @Component({ @@ -17,12 +17,6 @@ import { TranslateService } from '@ngx-translate/core'; styleUrls: ['./base-screen.component.scss'] }) export class BaseScreenComponent { - private _projectsView: boolean; - - get user() { - return this._userService.user; - } - constructor( readonly appStateService: AppStateService, readonly permissionsService: PermissionsService, @@ -41,10 +35,16 @@ export class BaseScreenComponent { }); } + private _projectsView: boolean; + get projectsView() { return this._projectsView; } + get user() { + return this._userService.user; + } + get showPendingDownloadsDot() { return this.fileDownloadService.hasPendingDownloads; } diff --git a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts index f3ade2a31..992991c75 100644 --- a/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts +++ b/apps/red-ui/src/app/components/downloads-list-screen/downloads-list-screen.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { FileDownloadService } from '../../modules/upload-download/services/file-download.service'; -import { DownloadStatusWrapper } from '../../modules/upload-download/model/download-status.wrapper'; +import { FileDownloadService } from '@upload-download/services/file-download.service'; +import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper'; import { DownloadControllerService } from '@redaction/red-ui-http'; @Component({ @@ -11,14 +11,14 @@ import { DownloadControllerService } from '@redaction/red-ui-http'; export class DownloadsListScreenComponent implements OnInit { constructor(readonly fileDownloadService: FileDownloadService, private readonly _downloadControllerService: DownloadControllerService) {} - ngOnInit(): void { - this.fileDownloadService.getDownloadStatus().subscribe(); - } - get noData(): boolean { return this.fileDownloadService.downloads.length === 0; } + ngOnInit(): void { + this.fileDownloadService.getDownloadStatus().subscribe(); + } + async downloadItem(download: DownloadStatusWrapper) { await this.fileDownloadService.performDownload(download); } diff --git a/apps/red-ui/src/app/components/notifications/notifications.component.html b/apps/red-ui/src/app/components/notifications/notifications.component.html index f7766cc1e..736aa49b7 100644 --- a/apps/red-ui/src/app/components/notifications/notifications.component.html +++ b/apps/red-ui/src/app/components/notifications/notifications.component.html @@ -1,12 +1,12 @@ - - + +
{{ day(group) }}
@@ -14,9 +14,9 @@
{{ eventTime(notification.eventTime) }}
diff --git a/apps/red-ui/src/app/components/notifications/notifications.component.ts b/apps/red-ui/src/app/components/notifications/notifications.component.ts index ec720d1eb..337e1cdc2 100644 --- a/apps/red-ui/src/app/components/notifications/notifications.component.ts +++ b/apps/red-ui/src/app/components/notifications/notifications.component.ts @@ -15,7 +15,11 @@ interface Notification { }) export class NotificationsComponent { notifications: Notification[] = [ - { message: 'This is a notification with longer text wrapping on multiple lines', eventTime: 1607340971000, read: false }, + { + message: 'This is a notification with longer text wrapping on multiple lines', + eventTime: 1607340971000, + read: false + }, { message: 'This is a link', eventTime: 1607254981000, read: true }, { message: 'This is a notification 1', eventTime: 1607254571000, read: false }, { message: 'Notification', eventTime: 1607385727000, read: true }, @@ -31,18 +35,6 @@ export class NotificationsComponent { return this.notifications.filter((notification) => !notification.read).length > 0; } - private _groupNotifications() { - const res = {}; - for (const notification of this.notifications) { - const date = moment(notification.eventTime).format('YYYY-MM-DD'); - if (!res[date]) res[date] = []; - res[date].push(notification); - } - for (const key of Object.keys(res)) { - this.groupedNotifications.push({ dateString: key, notifications: res[key] }); - } - } - day(group: { dateString: string; notifications: Notification[] }): string { moment.locale(this._translateService.currentLang); return moment(group.notifications[0].eventTime).calendar({ @@ -68,4 +60,16 @@ export class NotificationsComponent { $event.stopPropagation(); notification.read = !notification.read; } + + private _groupNotifications() { + const res = {}; + for (const notification of this.notifications) { + const date = moment(notification.eventTime).format('YYYY-MM-DD'); + if (!res[date]) res[date] = []; + res[date].push(notification); + } + for (const key of Object.keys(res)) { + this.groupedNotifications.push({ dateString: key, notifications: res[key] }); + } + } } diff --git a/apps/red-ui/src/app/components/user-profile/user-profile-screen.component.ts b/apps/red-ui/src/app/components/user-profile/user-profile-screen.component.ts index e84937260..955df78ee 100644 --- a/apps/red-ui/src/app/components/user-profile/user-profile-screen.component.ts +++ b/apps/red-ui/src/app/components/user-profile/user-profile-screen.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { UserService } from '../../services/user.service'; -import { PermissionsService } from '../../services/permissions.service'; -import { LanguageService } from '../../i18n/language.service'; +import { UserService } from '@services/user.service'; +import { PermissionsService } from '@services/permissions.service'; +import { LanguageService } from '@i18n/language.service'; import { TranslateService } from '@ngx-translate/core'; import { UserControllerService } from '@redaction/red-ui-http'; @@ -39,10 +39,6 @@ export class UserProfileScreenComponent implements OnInit { }); } - ngOnInit() { - this._initializeForm(); - } - get languageChanged(): boolean { return this._profileModel['language'] !== this.formGroup.get('language').value; } @@ -64,6 +60,10 @@ export class UserProfileScreenComponent implements OnInit { return this._translateService.langs; } + ngOnInit() { + this._initializeForm(); + } + async save(): Promise { this.viewReady = false; diff --git a/apps/red-ui/src/app/guards/composite-route.guard.ts b/apps/red-ui/src/app/guards/composite-route.guard.ts index a275cc828..0c134854e 100644 --- a/apps/red-ui/src/app/guards/composite-route.guard.ts +++ b/apps/red-ui/src/app/guards/composite-route.guard.ts @@ -1,7 +1,7 @@ import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { Injectable, Injector } from '@angular/core'; import { from, of } from 'rxjs'; -import { AppLoadStateService } from '../services/app-load-state.service'; +import { AppLoadStateService } from '@services/app-load-state.service'; @Injectable({ providedIn: 'root' diff --git a/apps/red-ui/src/app/models/file/annotation.permissions.ts b/apps/red-ui/src/app/models/file/annotation.permissions.ts index 64a7f005a..149f3dfdc 100644 --- a/apps/red-ui/src/app/models/file/annotation.permissions.ts +++ b/apps/red-ui/src/app/models/file/annotation.permissions.ts @@ -1,4 +1,4 @@ -import { UserWrapper } from '../../services/user.service'; +import { UserWrapper } from '@services/user.service'; import { AnnotationWrapper } from './annotation.wrapper'; export class AnnotationPermissions { @@ -16,6 +16,16 @@ export class AnnotationPermissions { canForceRedaction: boolean; + get canPerformMultipleRemoveActions() { + return ( + this.canMarkTextOnlyAsFalsePositive + + this.canMarkAsFalsePositive + + this.canRemoveOrSuggestToRemoveFromDictionary + + this.canRemoveOrSuggestToRemoveOnlyHere >= + 2 + ); + } + static forUser(isManagerAndOwner: boolean, user: UserWrapper, annotation: AnnotationWrapper) { const permissions: AnnotationPermissions = new AnnotationPermissions(); @@ -40,14 +50,4 @@ export class AnnotationPermissions { return permissions; } - - get canPerformMultipleRemoveActions() { - return ( - this.canMarkTextOnlyAsFalsePositive + - this.canMarkAsFalsePositive + - this.canRemoveOrSuggestToRemoveFromDictionary + - this.canRemoveOrSuggestToRemoveOnlyHere >= - 2 - ); - } } diff --git a/apps/red-ui/src/app/models/file/file-data.model.ts b/apps/red-ui/src/app/models/file/file-data.model.ts index 348bbb32e..f4b00dc77 100644 --- a/apps/red-ui/src/app/models/file/file-data.model.ts +++ b/apps/red-ui/src/app/models/file/file-data.model.ts @@ -9,7 +9,7 @@ import { ViewedPages } from '@redaction/red-ui-http'; import { FileStatusWrapper } from './file-status.wrapper'; -import { UserWrapper } from '../../services/user.service'; +import { UserWrapper } from '@services/user.service'; import { AnnotationWrapper } from './annotation.wrapper'; import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper'; import { ViewMode } from './view-mode'; diff --git a/apps/red-ui/src/app/models/file/file-status.wrapper.ts b/apps/red-ui/src/app/models/file/file-status.wrapper.ts index 198083885..f05e81f54 100644 --- a/apps/red-ui/src/app/models/file/file-status.wrapper.ts +++ b/apps/red-ui/src/app/models/file/file-status.wrapper.ts @@ -1,5 +1,5 @@ import { FileAttributesConfig, FileStatus } from '@redaction/red-ui-http'; -import { StatusSorter } from '../../utils/sorters/status-sorter'; +import { StatusSorter } from '@utils/sorters/status-sorter'; export class FileStatusWrapper { primaryAttribute: string; diff --git a/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts b/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts index 37604a15b..105363453 100644 --- a/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts +++ b/apps/red-ui/src/app/models/file/redaction-log-entry.wrapper.ts @@ -1,4 +1,4 @@ -import { Rectangle, Comment } from '@redaction/red-ui-http'; +import { Comment, Rectangle } from '@redaction/red-ui-http'; export interface RedactionLogEntryWrapper { color?: Array; diff --git a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts index 07bd55eb1..516e2c601 100644 --- a/apps/red-ui/src/app/modules/admin/admin-routing.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin-routing.module.ts @@ -1,12 +1,12 @@ import { NgModule } from '@angular/core'; import { AuthGuard } from '../auth/auth.guard'; -import { CompositeRouteGuard } from '../../guards/composite-route.guard'; +import { CompositeRouteGuard } from '@guards/composite-route.guard'; import { RedRoleGuard } from '../auth/red-role.guard'; -import { AppStateGuard } from '../../state/app-state.guard'; +import { AppStateGuard } from '@state/app-state.guard'; import { RuleSetsListingScreenComponent } from './screens/rule-sets-listing/rule-sets-listing-screen.component'; import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component'; import { DictionaryOverviewScreenComponent } from './screens/dictionary-overview/dictionary-overview-screen.component'; -import { PendingChangesGuard } from '../../guards/can-deactivate.guard'; +import { PendingChangesGuard } from '@guards/can-deactivate.guard'; import { RulesScreenComponent } from './screens/rules/rules-screen.component'; import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component'; import { WatermarkScreenComponent } from './screens/watermark/watermark-screen.component'; diff --git a/apps/red-ui/src/app/modules/admin/admin.module.ts b/apps/red-ui/src/app/modules/admin/admin.module.ts index b67417b41..bdc0e2bde 100644 --- a/apps/red-ui/src/app/modules/admin/admin.module.ts +++ b/apps/red-ui/src/app/modules/admin/admin.module.ts @@ -2,7 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { AdminRoutingModule } from './admin-routing.module'; import { RulesScreenComponent } from './screens/rules/rules-screen.component'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@shared/shared.module'; import { RuleSetsListingScreenComponent } from './screens/rule-sets-listing/rule-sets-listing-screen.component'; import { AuditScreenComponent } from './screens/audit/audit-screen.component'; import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component'; diff --git a/apps/red-ui/src/app/modules/admin/components/breadcrumbs/admin-breadcrumbs.component.ts b/apps/red-ui/src/app/modules/admin/components/breadcrumbs/admin-breadcrumbs.component.ts index 54f2c75ba..31433aa88 100644 --- a/apps/red-ui/src/app/modules/admin/components/breadcrumbs/admin-breadcrumbs.component.ts +++ b/apps/red-ui/src/app/modules/admin/components/breadcrumbs/admin-breadcrumbs.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; -import { UserPreferenceService } from '../../../../services/user-preference.service'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { AppStateService } from '@state/app-state.service'; +import { UserPreferenceService } from '@services/user-preference.service'; +import { PermissionsService } from '@services/permissions.service'; @Component({ selector: 'redaction-admin-breadcrumbs', diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss b/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss index 708586e9e..7ee754487 100644 --- a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss +++ b/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.scss @@ -21,6 +21,7 @@ &:focus { outline: none; } + &.hidden { display: none; } diff --git a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts b/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts index 14ad69d13..879574358 100644 --- a/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts +++ b/apps/red-ui/src/app/modules/admin/components/combo-chart/combo-chart.component.ts @@ -1,8 +1,8 @@ -import { Component, Input, ViewEncapsulation, Output, EventEmitter, ViewChild, HostListener, ContentChild, TemplateRef } from '@angular/core'; +import { Component, ContentChild, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { curveLinear } from 'd3-shape'; import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale'; -import { BaseChartComponent, LineSeriesComponent, ViewDimensions, ColorHelper, calculateViewDimensions } from '@swimlane/ngx-charts'; +import { BaseChartComponent, calculateViewDimensions, ColorHelper, LineSeriesComponent, ViewDimensions } from '@swimlane/ngx-charts'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector diff --git a/apps/red-ui/src/app/modules/admin/components/rule-set-actions/rule-set-actions.component.ts b/apps/red-ui/src/app/modules/admin/components/rule-set-actions/rule-set-actions.component.ts index 4247c69ec..b3a9f2741 100644 --- a/apps/red-ui/src/app/modules/admin/components/rule-set-actions/rule-set-actions.component.ts +++ b/apps/red-ui/src/app/modules/admin/components/rule-set-actions/rule-set-actions.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { AppStateService } from '../../../../state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { AppStateService } from '@state/app-state.service'; import { Router } from '@angular/router'; import { AdminDialogService } from '../../services/admin-dialog.service'; diff --git a/apps/red-ui/src/app/modules/admin/components/side-nav/side-nav.component.ts b/apps/red-ui/src/app/modules/admin/components/side-nav/side-nav.component.ts index 115c6fe83..82c236d6c 100644 --- a/apps/red-ui/src/app/modules/admin/components/side-nav/side-nav.component.ts +++ b/apps/red-ui/src/app/modules/admin/components/side-nav/side-nav.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { UserPreferenceService } from '../../../../services/user-preference.service'; -import { AppStateService } from '../../../../state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { UserPreferenceService } from '@services/user-preference.service'; +import { AppStateService } from '@state/app-state.service'; @Component({ selector: 'redaction-side-nav', diff --git a/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.ts b/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.ts index 2d890d907..5b6a4c578 100644 --- a/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.ts +++ b/apps/red-ui/src/app/modules/admin/components/users-stats/users-stats.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; @Component({ selector: 'redaction-users-stats', diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts index 524250d31..3326227a9 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http'; import { Observable } from 'rxjs'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; @Component({ diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html index a08394b04..c2492ef29 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.html @@ -7,7 +7,7 @@
- +
@@ -15,8 +15,8 @@
@@ -31,11 +31,11 @@
- {{ 'add-edit-file-attribute.form.read-only' | translate }} + {{ 'add-edit-file-attribute.form.read-only' | translate }}
- + {{ 'add-edit-file-attribute.form.primary' | translate }}
@@ -48,5 +48,5 @@
- + diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts index 4c5f4e298..c68b603cc 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component.ts @@ -1,6 +1,6 @@ import { Component, Inject } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { FileAttributeConfig } from '@redaction/red-ui-http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts index 65402e65e..e16e4d803 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component.ts @@ -1,11 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import * as moment from 'moment'; import { Moment } from 'moment'; import { RuleSetControllerService, RuleSetModel } from '@redaction/red-ui-http'; -import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils'; +import { applyIntervalConstraints } from '@utils/date-inputs-utils'; @Component({ selector: 'redaction-add-edit-rule-set-dialog', @@ -53,28 +53,6 @@ export class AddEditRuleSetDialogComponent { }); } - private _applyValidityIntervalConstraints(value): boolean { - if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) { - return true; - } - - this._previousValidFrom = this.ruleSetForm.get('validFrom').value; - this._previousValidTo = this.ruleSetForm.get('validTo').value; - return false; - } - - private _requiredIfValidator(predicate) { - return (formControl) => { - if (!formControl.parent) { - return null; - } - if (predicate()) { - return Validators.required(formControl); - } - return null; - }; - } - get changed(): boolean { if (!this.ruleSet) return true; @@ -113,4 +91,26 @@ export class AddEditRuleSetDialogComponent { await this._appStateService.loadDictionaryData(); this.dialogRef.close({ ruleSet }); } + + private _applyValidityIntervalConstraints(value): boolean { + if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) { + return true; + } + + this._previousValidFrom = this.ruleSetForm.get('validFrom').value; + this._previousValidTo = this.ruleSetForm.get('validTo').value; + return false; + } + + private _requiredIfValidator(predicate) { + return (formControl) => { + if (!formControl.parent) { + return null; + } + if (predicate()) { + return Validators.required(formControl); + } + return null; + }; + } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts index de168e68b..6a1bc21a4 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/add-edit-user-dialog.component.ts @@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { User } from '@redaction/red-ui-http'; -import { UserService } from '../../../../services/user.service'; +import { UserService } from '@services/user.service'; @Component({ selector: 'redaction-add-edit-user-dialog', @@ -47,19 +47,6 @@ export class AddEditUserDialogComponent { this._setRolesRequirements(); } - private _setRolesRequirements() { - for (const key of Object.keys(this._ROLE_REQUIREMENTS)) { - this.userForm.controls[key].valueChanges.subscribe((checked) => { - if (checked) { - this.userForm.patchValue({ [this._ROLE_REQUIREMENTS[key]]: true }); - this.userForm.controls[this._ROLE_REQUIREMENTS[key]].disable(); - } else { - this.userForm.controls[this._ROLE_REQUIREMENTS[key]].enable(); - } - }); - } - } - get changed(): boolean { if (!this.user) return true; @@ -72,6 +59,15 @@ export class AddEditUserDialogComponent { return false; } + get activeRoles(): string[] { + return this.ROLES.reduce((acc, role) => { + if (this.userForm.get(role).value) { + acc.push(role); + } + return acc; + }, []); + } + async save() { this.dialogRef.close({ action: this.user ? 'UPDATE' : 'CREATE', @@ -83,12 +79,16 @@ export class AddEditUserDialogComponent { this.dialogRef.close('DELETE'); } - get activeRoles(): string[] { - return this.ROLES.reduce((acc, role) => { - if (this.userForm.get(role).value) { - acc.push(role); - } - return acc; - }, []); + private _setRolesRequirements() { + for (const key of Object.keys(this._ROLE_REQUIREMENTS)) { + this.userForm.controls[key].valueChanges.subscribe((checked) => { + if (checked) { + this.userForm.patchValue({ [this._ROLE_REQUIREMENTS[key]]: true }); + this.userForm.controls[this._ROLE_REQUIREMENTS[key]].disable(); + } else { + this.userForm.controls[this._ROLE_REQUIREMENTS[key]].enable(); + } + }); + } } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component.ts index 3ca2443ac..85faf8830 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { FileAttributeConfig } from '@redaction/red-ui-http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; @@ -28,6 +28,10 @@ export class ConfirmDeleteFileAttributeDialogComponent { return this.checkboxes[0].value && this.checkboxes[1].value; } + get type(): 'bulk' | 'single' { + return !this.fileAttribute ? 'bulk' : 'single'; + } + deleteFileAttribute() { if (this.valid) { this.dialogRef.close(true); @@ -36,10 +40,6 @@ export class ConfirmDeleteFileAttributeDialogComponent { } } - get type(): 'bulk' | 'single' { - return !this.fileAttribute ? 'bulk' : 'single'; - } - cancel() { this.dialogRef.close(); } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component.ts index dc04b30f3..46d8c05b6 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { User } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; @Component({ selector: 'redaction-confirm-delete-users-dialog', @@ -31,6 +31,14 @@ export class ConfirmDeleteUsersDialogComponent { }).length; } + get valid() { + return this.checkboxes[0].value && this.checkboxes[1].value; + } + + get type(): 'bulk' | 'single' { + return this.users.length > 1 ? 'bulk' : 'single'; + } + async deleteUser() { if (this.valid) { this.dialogRef.close(true); @@ -39,15 +47,7 @@ export class ConfirmDeleteUsersDialogComponent { } } - get valid() { - return this.checkboxes[0].value && this.checkboxes[1].value; - } - cancel() { this.dialogRef.close(); } - - get type(): 'bulk' | 'single' { - return this.users.length > 1 ? 'bulk' : 'single'; - } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts index ecc3fdfab..ff00039a9 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Colors, DictionaryControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -13,9 +13,9 @@ import { TranslateService } from '@ngx-translate/core'; export class EditColorDialogComponent { readonly colors: Colors; readonly colorKey: string; + colorForm: FormGroup; private readonly _initialColor: string; private readonly _ruleSetId: string; - colorForm: FormGroup; constructor( private readonly _formBuilder: FormBuilder, diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts index 6e3474b2e..3187b5e94 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/active-fields-listing/active-fields-listing.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; -import { BaseListingComponent } from '../../../../shared/base/base-listing.component'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; import { Field } from '../file-attributes-csv-import-dialog.component'; import { FileAttributeConfig } from '@redaction/red-ui-http'; diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts index 823e05ba0..3299cf870 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts @@ -1,14 +1,14 @@ import { Component, Inject, Injector, ViewChild } from '@angular/core'; import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import * as Papa from 'papaparse'; import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; export interface Field { @@ -28,8 +28,6 @@ export interface Field { styleUrls: ['./file-attributes-csv-import-dialog.component.scss'] }) export class FileAttributesCsvImportDialogComponent extends BaseListingComponent { - protected readonly _searchKey = 'csvColumn'; - csvFile: File; ruleSetId: string; parseResult: { data: any[]; errors: any[]; meta: any; fields: Field[] }; @@ -42,8 +40,8 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent keepPreview = false; columnSample = []; initialParseConfig: { delimiter?: string; encoding?: string } = {}; - @ViewChild(CdkVirtualScrollViewport, { static: false }) cdkVirtualScrollViewport: CdkVirtualScrollViewport; + protected readonly _searchKey = 'csvColumn'; constructor( private readonly _appStateService: AppStateService, @@ -67,13 +65,11 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent this.readFile(); } - private _autocompleteStringValidator(): ValidatorFn { - return (control: AbstractControl): { [key: string]: any } | null => { - if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) { - return null; /* valid option selected */ - } - return { invalidAutocompleteString: { value: control.value } }; - }; + get changedParseConfig(): boolean { + return ( + this.initialParseConfig.delimiter !== this.baseConfigForm.get('delimiter').value || + this.initialParseConfig.encoding !== this.baseConfigForm.get('encoding').value + ); } readFile() { @@ -161,19 +157,6 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent } } - private _buildAttribute(csvColumn: string): Field { - const sample = this.getSample(csvColumn); - const isNumber = sample && !isNaN(sample); - return { - csvColumn, - name: csvColumn, - temporaryName: csvColumn, - type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT, - readonly: false, - primaryAttribute: false - }; - } - activateAll() { this.activeFields = [...this.allEntities]; } @@ -239,10 +222,25 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent }, 0); } - get changedParseConfig(): boolean { - return ( - this.initialParseConfig.delimiter !== this.baseConfigForm.get('delimiter').value || - this.initialParseConfig.encoding !== this.baseConfigForm.get('encoding').value - ); + private _autocompleteStringValidator(): ValidatorFn { + return (control: AbstractControl): { [key: string]: any } | null => { + if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) { + return null; /* valid option selected */ + } + return { invalidAutocompleteString: { value: control.value } }; + }; + } + + private _buildAttribute(csvColumn: string): Field { + const sample = this.getSample(csvColumn); + const isNumber = sample && !isNaN(sample); + return { + csvColumn, + name: csvColumn, + temporaryName: csvColumn, + type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT, + readonly: false, + primaryAttribute: false + }; } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts index f123463d2..6c54118ab 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/smtp-auth-dialog/smtp-auth-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { UserService } from '../../../../services/user.service'; +import { UserService } from '@services/user.service'; import { SMTPConfigurationModel } from '@redaction/red-ui-http'; @Component({ diff --git a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts index 972f51c19..224bf5237 100644 --- a/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/audit/audit-screen.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { FormBuilder, FormGroup } from '@angular/forms'; import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http'; import { TranslateService } from '@ngx-translate/core'; import { Moment } from 'moment'; -import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils'; +import { applyIntervalConstraints } from '@utils/date-inputs-utils'; const PAGE_SIZE = 50; @@ -49,6 +49,17 @@ export class AuditScreenComponent { this._fetchData(); } + get totalPages(): number { + if (!this.logs) { + return 0; + } + return Math.ceil(this.logs.totalHits / PAGE_SIZE); + } + + pageChanged(page: number) { + this._fetchData(page); + } + private _updateDateFilters(value): boolean { if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) { return true; @@ -93,15 +104,4 @@ export class AuditScreenComponent { this.viewReady = true; }); } - - get totalPages(): number { - if (!this.logs) { - return 0; - } - return Math.ceil(this.logs.totalHits / PAGE_SIZE); - } - - pageChanged(page: number) { - this._fetchData(page); - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts index b02ed44b1..e3f98d3b1 100644 --- a/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/default-colors/default-colors-screen.component.ts @@ -1,10 +1,10 @@ import { Component, Injector } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { Colors, DictionaryControllerService } from '@redaction/red-ui-http'; import { ActivatedRoute } from '@angular/router'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { AdminDialogService } from '../../services/admin-dialog.service'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; @Component({ selector: 'redaction-default-colors-screen', @@ -12,9 +12,8 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen styleUrls: ['./default-colors-screen.component.scss'] }) export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: string; value: string }> { - protected readonly _sortKey = 'default-colors'; - viewReady = false; + protected readonly _sortKey = 'default-colors'; private _colorsObj: Colors; constructor( @@ -34,6 +33,13 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st await this._appStateService.loadAllRuleSets(); } + openEditColorDialog($event: any, color: { key: string; value: string }) { + $event.stopPropagation(); + this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => { + this._loadColors(); + }); + } + private _loadColors() { this._dictionaryControllerService .getColors(this._appStateService.activeRuleSetId) @@ -47,11 +53,4 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st this.viewReady = true; }); } - - openEditColorDialog($event: any, color: { key: string; value: string }) { - $event.stopPropagation(); - this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => { - this._loadColors(); - }); - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts index 20645a519..c9997ae8e 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-listing/dictionary-listing-screen.component.ts @@ -1,13 +1,13 @@ import { Component, Injector, OnInit } from '@angular/core'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { defaultIfEmpty, tap } from 'rxjs/operators'; import { forkJoin } from 'rxjs'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { ActivatedRoute } from '@angular/router'; import { AdminDialogService } from '../../services/admin-dialog.service'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; @Component({ selector: 'redaction-dictionary-listing-screen', @@ -15,13 +15,12 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen styleUrls: ['./dictionary-listing-screen.component.scss'] }) export class DictionaryListingScreenComponent extends BaseListingComponent implements OnInit { + viewReady = false; + chartData: DoughnutChartConfig[] = []; protected readonly _searchKey = 'label'; protected readonly _selectionKey = 'type'; protected readonly _sortKey = 'dictionary-listing'; - viewReady = false; - chartData: DoughnutChartConfig[] = []; - constructor( private readonly _dialogService: AdminDialogService, private readonly _dictionaryControllerService: DictionaryControllerService, @@ -38,6 +37,23 @@ export class DictionaryListingScreenComponent extends BaseListingComponent { + if (newDictionary) { + await this._appStateService.loadDictionaryData(); + this._loadDictionaryData(); + } + }); + } + + openDeleteDictionaryDialog($event: any, dict: TypeValue) { + this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => { + await this._appStateService.loadDictionaryData(); + this._loadDictionaryData(); + }); + } + private _loadDictionaryData() { const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId]; this.allEntities = Object.keys(appStateDictionaryData) @@ -71,21 +87,4 @@ export class DictionaryListingScreenComponent extends BaseListingComponent (a.label < b.label ? -1 : 1)); this.viewReady = true; } - - openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) { - $event?.stopPropagation(); - this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => { - if (newDictionary) { - await this._appStateService.loadDictionaryData(); - this._loadDictionaryData(); - } - }); - } - - openDeleteDictionaryDialog($event: any, dict: TypeValue) { - this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => { - await this._appStateService.loadDictionaryData(); - this._loadDictionaryData(); - }); - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts index f52d97867..5713878c1 100644 --- a/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/dictionary-overview/dictionary-overview-screen.component.ts @@ -1,15 +1,15 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { DictionaryControllerService, RuleSetModel, TypeValue } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; import { ActivatedRoute, Router } from '@angular/router'; import { AceEditorComponent } from 'ng2-ace-editor'; -import { debounce } from '../../../../utils/debounce'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { debounce } from '@utils/debounce'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { saveAs } from 'file-saver'; -import { ComponentHasChanges } from '../../../../guards/can-deactivate.guard'; +import { ComponentHasChanges } from '@guards/can-deactivate.guard'; import { FormBuilder, FormGroup } from '@angular/forms'; import { AdminDialogService } from '../../services/admin-dialog.service'; diff --git a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts index 08061e82a..a22970fd6 100644 --- a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; import { DigitalSignature, DigitalSignatureControllerService } from '@redaction/red-ui-http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { lastIndexOfEnd } from '../../../../utils/functions'; +import { PermissionsService } from '@services/permissions.service'; +import { lastIndexOfEnd } from '@utils/functions'; @Component({ selector: 'redaction-digital-signature-screen', @@ -28,6 +28,10 @@ export class DigitalSignatureScreenComponent { this.loadDigitalSignatureAndInitializeForm(); } + get hasDigitalSignatureSet() { + return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value; + } + saveDigitalSignature() { const digitalSignature = { ...this.digitalSignatureForm.getRawValue() @@ -119,6 +123,8 @@ export class DigitalSignatureScreenComponent { }); } + formChanged() {} + private _initForm() { this.digitalSignatureForm = this._formBuilder.group({ certificateName: [this.digitalSignature.certificateName, Validators.required], @@ -129,10 +135,4 @@ export class DigitalSignatureScreenComponent { base64EncodedPrivateKey: this.digitalSignatureExists ? null : [this.digitalSignature.base64EncodedPrivateKey, Validators.required] }); } - - get hasDigitalSignatureSet() { - return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value; - } - - formChanged() {} } diff --git a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts index 31482843e..d81a256b0 100644 --- a/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/file-attributes-listing/file-attributes-listing-screen.component.ts @@ -1,10 +1,10 @@ import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { ActivatedRoute } from '@angular/router'; import { AdminDialogService } from '../../services/admin-dialog.service'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; @Component({ selector: 'redaction-file-attributes-listing-screen', @@ -12,13 +12,11 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen styleUrls: ['./file-attributes-listing-screen.component.scss'] }) export class FileAttributesListingScreenComponent extends BaseListingComponent implements OnInit { + viewReady = false; + loading = false; protected readonly _searchKey = 'label'; protected readonly _selectionKey = 'id'; protected readonly _sortKey = 'file-attributes-listing'; - - viewReady = false; - loading = false; - private _existingConfiguration: FileAttributesConfig; @ViewChild('fileInput') private _fileInput: ElementRef; @@ -39,19 +37,6 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent { @@ -82,4 +67,17 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent { + if (newRuleSet) { + this.loadRuleSetsData(); + } + }); + } + private _loadRuleSetStats() { this.allEntities.forEach((rs) => { const dictionaries = this._appStateService.dictionaryData[rs.ruleSetId]; @@ -50,12 +58,4 @@ export class RuleSetsListingScreenComponent extends BaseListingComponent { - if (newRuleSet) { - this.loadRuleSetsData(); - } - }); - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts index 01436cf43..cc5b75b16 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts @@ -1,13 +1,13 @@ import { Component, ElementRef, ViewChild } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { AceEditorComponent } from 'ng2-ace-editor'; import { RulesControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { saveAs } from 'file-saver'; -import { ComponentHasChanges } from '../../../../guards/can-deactivate.guard'; +import { ComponentHasChanges } from '@guards/can-deactivate.guard'; import { ActivatedRoute } from '@angular/router'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; declare let ace; @@ -45,16 +45,8 @@ export class RulesScreenComponent extends ComponentHasChanges { this._initialize(); } - private _initialize() { - this._rulesControllerService.downloadRules(this._appStateService.activeRuleSetId).subscribe( - (rules) => { - this.rules = rules.rules; - this.revert(); - }, - () => { - this.processing = false; - } - ); + get hasChanges(): boolean { + return this.activeEditMarkers.length > 0; } textChanged($event: any) { @@ -82,10 +74,6 @@ export class RulesScreenComponent extends ComponentHasChanges { } } - get hasChanges(): boolean { - return this.activeEditMarkers.length > 0; - } - async save(): Promise { this.processing = true; this._rulesControllerService @@ -136,4 +124,16 @@ export class RulesScreenComponent extends ComponentHasChanges { fileReader.readAsText(file); } } + + private _initialize() { + this._rulesControllerService.downloadRules(this._appStateService.activeRuleSetId).subscribe( + (rules) => { + this.rules = rules.rules; + this.revert(); + }, + () => { + this.processing = false; + } + ); + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts index e1e188d3f..3a1ae84f1 100644 --- a/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AdminDialogService } from '../../services/admin-dialog.service'; import { SmtpConfigurationControllerService, SMTPConfigurationModel } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; @Component({ @@ -46,19 +46,6 @@ export class SmtpConfigScreenComponent implements OnInit { } }); } - async ngOnInit() { - await this._loadData(); - } - - private async _loadData() { - try { - this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise(); - this.configForm.patchValue(this._initialValue, { emitEvent: false }); - } catch (e) { - } finally { - this.viewReady = true; - } - } get changed(): boolean { if (!this._initialValue) return true; @@ -72,6 +59,10 @@ export class SmtpConfigScreenComponent implements OnInit { return false; } + async ngOnInit() { + await this._loadData(); + } + async save() { this.viewReady = false; await this._smtpConfigService.updateSMTPConfiguration(this.configForm.getRawValue()).toPromise(); @@ -104,4 +95,14 @@ export class SmtpConfigScreenComponent implements OnInit { this.viewReady = true; } } + + private async _loadData() { + try { + this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise(); + this.configForm.patchValue(this._initialValue, { emitEvent: false }); + } catch (e) { + } finally { + this.viewReady = true; + } + } } diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts index 519687e72..918f4d6d0 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts @@ -1,12 +1,12 @@ import { Component, Injector, OnInit } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { UserService } from '../../../../services/user.service'; +import { PermissionsService } from '@services/permissions.service'; +import { UserService } from '@services/user.service'; import { User, UserControllerService } from '@redaction/red-ui-http'; import { AdminDialogService } from '../../services/admin-dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { TranslateChartService } from '../../../../services/translate-chart.service'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { TranslateChartService } from '@services/translate-chart.service'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; @Component({ selector: 'redaction-user-listing-screen', @@ -14,12 +14,11 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen styleUrls: ['./user-listing-screen.component.scss'] }) export class UserListingScreenComponent extends BaseListingComponent implements OnInit { - protected readonly _selectionKey = 'userId'; - viewReady = false; loading = false; collapsedDetails = false; chartData: DoughnutChartConfig[] = []; + protected readonly _selectionKey = 'userId'; constructor( readonly permissionsService: PermissionsService, @@ -33,12 +32,12 @@ export class UserListingScreenComponent extends BaseListingComponent imple super(_injector); } - async ngOnInit() { - await this._loadData(); + get canDeleteSelected(): boolean { + return this.selectedEntitiesIds.indexOf(this.userService.userId) === -1; } - protected _searchField(user: any): string { - return this.userService.getName(user); + async ngOnInit() { + await this._loadData(); } openAddEditUserDialog($event: MouseEvent, user?: User) { @@ -67,6 +66,29 @@ export class UserListingScreenComponent extends BaseListingComponent imple }); } + getDisplayRoles(user: User) { + return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE'); + } + + async toggleActive(user: User) { + this.loading = true; + user.roles = this.userService.isActive(user) ? [] : ['RED_USER']; + await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise(); + await this._loadData(); + } + + toggleCollapsedDetails() { + this.collapsedDetails = !this.collapsedDetails; + } + + async bulkDelete() { + this.openDeleteUserDialog(this.allEntities.filter((u) => this.isEntitySelected(u))); + } + + protected _searchField(user: any): string { + return this.userService.getName(user); + } + private async _loadData() { this.allEntities = await this._userControllerService.getAllUsers().toPromise(); this._executeSearchImmediately(); @@ -111,27 +133,4 @@ export class UserListingScreenComponent extends BaseListingComponent imple ].filter((type) => type.value > 0) ); } - - getDisplayRoles(user: User) { - return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE'); - } - - async toggleActive(user: User) { - this.loading = true; - user.roles = this.userService.isActive(user) ? [] : ['RED_USER']; - await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise(); - await this._loadData(); - } - - toggleCollapsedDetails() { - this.collapsedDetails = !this.collapsedDetails; - } - - async bulkDelete() { - this.openDeleteUserDialog(this.allEntities.filter((u) => this.isEntitySelected(u))); - } - - get canDeleteSelected(): boolean { - return this.selectedEntitiesIds.indexOf(this.userService.userId) === -1; - } } diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts index cfdacdf4a..76a2e0f57 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts @@ -1,16 +1,16 @@ import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import WebViewer, { WebViewerInstance } from '@pdftron/webviewer'; -import { AppStateService } from '../../../../state/app-state.service'; -import { environment } from '../../../../../environments/environment'; +import { AppStateService } from '@state/app-state.service'; +import { environment } from '@environments/environment'; import { HttpClient } from '@angular/common/http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { debounce } from '../../../../utils/debounce'; +import { debounce } from '@utils/debounce'; import { WatermarkControllerService, WatermarkModel } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute } from '@angular/router'; -import { hexToRgb } from '../../../../utils/functions'; +import { hexToRgb } from '@utils/functions'; import { BASE_HREF } from '../../../../tokens'; export const DEFAULT_WATERMARK: WatermarkModel = { @@ -28,26 +28,12 @@ export const DEFAULT_WATERMARK: WatermarkModel = { styleUrls: ['./watermark-screen.component.scss'] }) export class WatermarkScreenComponent implements OnInit { - private _instance: WebViewerInstance; - private _watermark: WatermarkModel = {}; - - @ViewChild('viewer', { static: true }) - private _viewer: ElementRef; - viewReady = false; configForm: FormGroup; - - get changed(): boolean { - if (this._watermark === DEFAULT_WATERMARK) { - return true; - } - for (const key of Object.keys(this._watermark)) { - if (this._watermark[key] !== this.configForm.get(key)?.value) { - return true; - } - } - return false; - } + private _instance: WebViewerInstance; + private _watermark: WatermarkModel = {}; + @ViewChild('viewer', { static: true }) + private _viewer: ElementRef; constructor( readonly permissionsService: PermissionsService, @@ -65,23 +51,20 @@ export class WatermarkScreenComponent implements OnInit { this._initForm(); } - ngOnInit(): void { - this._loadWatermark(); + get changed(): boolean { + if (this._watermark === DEFAULT_WATERMARK) { + return true; + } + for (const key of Object.keys(this._watermark)) { + if (this._watermark[key] !== this.configForm.get(key)?.value) { + return true; + } + } + return false; } - private _loadWatermark() { - this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe( - (watermark) => { - this._watermark = watermark; - this.configForm.setValue({ ...this._watermark }); - this._loadViewer(); - }, - () => { - this._watermark = DEFAULT_WATERMARK; - this.configForm.setValue({ ...this._watermark }); - this._loadViewer(); - } - ); + ngOnInit(): void { + this._loadWatermark(); } @debounce() @@ -120,6 +103,28 @@ export class WatermarkScreenComponent implements OnInit { triggerChanges() {} + setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) { + if (!this.configForm.get(type).disabled) { + this.configForm.get(type).setValue(value); + this.configChanged(); + } + } + + private _loadWatermark() { + this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe( + (watermark) => { + this._watermark = watermark; + this.configForm.setValue({ ...this._watermark }); + this._loadViewer(); + }, + () => { + this._watermark = DEFAULT_WATERMARK; + this.configForm.setValue({ ...this._watermark }); + this._loadViewer(); + } + ); + } + private _loadViewer() { if (!this._instance) { WebViewer( @@ -219,13 +224,6 @@ export class WatermarkScreenComponent implements OnInit { }); } - setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) { - if (!this.configForm.get(type).disabled) { - this.configForm.get(type).setValue(value); - this.configChanged(); - } - } - private _convertFont(fontType: any): number { switch (fontType) { case 'times-new-roman': diff --git a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts index 321651d30..447a76fd7 100644 --- a/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts +++ b/apps/red-ui/src/app/modules/admin/services/admin-dialog.service.ts @@ -16,9 +16,9 @@ import { import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component'; import { AddEditDictionaryDialogComponent } from '../dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component'; import { AddEditRuleSetDialogComponent } from '../dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component'; -import { NotificationService } from '../../../services/notification.service'; -import { ConfirmationDialogComponent } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component'; -import { AppStateService } from '../../../state/app-state.service'; +import { NotificationService } from '@services/notification.service'; +import { ConfirmationDialogComponent } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; +import { AppStateService } from '@state/app-state.service'; import { ConfirmDeleteFileAttributeDialogComponent } from '../dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component'; import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component'; import { TranslateService } from '@ngx-translate/core'; diff --git a/apps/red-ui/src/app/modules/auth/auth.guard.ts b/apps/red-ui/src/app/modules/auth/auth.guard.ts index 1a08305f8..e680cd274 100644 --- a/apps/red-ui/src/app/modules/auth/auth.guard.ts +++ b/apps/red-ui/src/app/modules/auth/auth.guard.ts @@ -1,9 +1,9 @@ import { Inject, Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router'; import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular'; -import { UserService } from '../../services/user.service'; -import { AppLoadStateService } from '../../services/app-load-state.service'; -import { AppConfigKey, AppConfigService } from '../app-config/app-config.service'; +import { UserService } from '@services/user.service'; +import { AppLoadStateService } from '@services/app-load-state.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { BASE_HREF } from '../../tokens'; @Injectable({ diff --git a/apps/red-ui/src/app/modules/auth/auth.module.ts b/apps/red-ui/src/app/modules/auth/auth.module.ts index 695b4e874..eead711f4 100644 --- a/apps/red-ui/src/app/modules/auth/auth.module.ts +++ b/apps/red-ui/src/app/modules/auth/auth.module.ts @@ -1,14 +1,15 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { HttpClientModule } from '@angular/common/http'; -import { AppConfigModule } from '../app-config/app-config.module'; +import { AppConfigModule } from '@app-config/app-config.module'; import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular'; -import { AppConfigKey, AppConfigService } from '../app-config/app-config.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { BASE_HREF } from '../../tokens'; export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) { - return () => appConfigService + return () => + appConfigService .loadAppConfig() .toPromise() .then(() => { diff --git a/apps/red-ui/src/app/modules/auth/red-role.guard.ts b/apps/red-ui/src/app/modules/auth/red-role.guard.ts index 67c7482c2..a457bfb82 100644 --- a/apps/red-ui/src/app/modules/auth/red-role.guard.ts +++ b/apps/red-ui/src/app/modules/auth/red-role.guard.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; -import { UserService } from '../../services/user.service'; -import { AppLoadStateService } from '../../services/app-load-state.service'; +import { UserService } from '@services/user.service'; +import { AppLoadStateService } from '@services/app-load-state.service'; import { Observable } from 'rxjs'; @Injectable({ diff --git a/apps/red-ui/src/app/modules/projects/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/annotation-actions/annotation-actions.component.ts index a2d926125..a24e0ca00 100644 --- a/apps/red-ui/src/app/modules/projects/components/annotation-actions/annotation-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/annotation-actions/annotation-actions.component.ts @@ -1,8 +1,8 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; -import { AppStateService } from '../../../../state/app-state.service'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { AnnotationPermissions } from '../../../../models/file/annotation.permissions'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { AnnotationPermissions } from '@models/file/annotation.permissions'; import { AnnotationActionsService } from '../../services/annotation-actions.service'; import { Annotations, WebViewerInstance } from '@pdftron/webviewer'; @@ -27,6 +27,10 @@ export class AnnotationActionsComponent implements OnInit { private _permissionsService: PermissionsService ) {} + get viewerAnnotation(): Annotations.Annotation { + return this.viewer.annotManager.getAnnotationById(this.annotation.id); + } + ngOnInit(): void { this.annotationPermissions = AnnotationPermissions.forUser( this._permissionsService.isManagerAndOwner(), @@ -35,10 +39,6 @@ export class AnnotationActionsComponent implements OnInit { ); } - get viewerAnnotation(): Annotations.Annotation { - return this.viewer.annotManager.getAnnotationById(this.annotation.id); - } - hideAnnotation($event: MouseEvent) { $event.stopPropagation(); this.viewer.annotManager.hideAnnotations([this.viewerAnnotation]); diff --git a/apps/red-ui/src/app/modules/projects/components/annotation-remove-actions/annotation-remove-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/annotation-remove-actions/annotation-remove-actions.component.ts index 886b51e7a..b25872e53 100644 --- a/apps/red-ui/src/app/modules/projects/components/annotation-remove-actions/annotation-remove-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/annotation-remove-actions/annotation-remove-actions.component.ts @@ -1,9 +1,9 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; +import { AppStateService } from '@state/app-state.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationActionsService } from '../../services/annotation-actions.service'; -import { AnnotationPermissions } from '../../../../models/file/annotation.permissions'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { AnnotationPermissions } from '@models/file/annotation.permissions'; +import { PermissionsService } from '@services/permissions.service'; import { MatMenuTrigger } from '@angular/material/menu'; @Component({ diff --git a/apps/red-ui/src/app/modules/projects/components/bulk-actions/project-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/bulk-actions/project-overview-bulk-actions.component.ts index 531ae3ce9..88f35737c 100644 --- a/apps/red-ui/src/app/modules/projects/components/bulk-actions/project-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/bulk-actions/project-overview-bulk-actions.component.ts @@ -1,12 +1,12 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; -import { UserService } from '../../../../services/user.service'; +import { AppStateService } from '@state/app-state.service'; +import { UserService } from '@services/user.service'; import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; +import { PermissionsService } from '@services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { FileActionService } from '../../services/file-action.service'; import { Observable } from 'rxjs'; -import { StatusOverlayService } from '../../../upload-download/services/status-overlay.service'; +import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; @Component({ @@ -16,8 +16,8 @@ import { ProjectsDialogService } from '../../services/projects-dialog.service'; }) export class ProjectOverviewBulkActionsComponent { @Input() selectedFileIds: string[]; - @Output() private _reload = new EventEmitter(); loading = false; + @Output() private _reload = new EventEmitter(); constructor( private readonly _appStateService: AppStateService, @@ -67,6 +67,30 @@ export class ProjectOverviewBulkActionsComponent { return this.selectedFiles.map((file) => file.fileStatus.status); } + // Under review + get canSetToUnderReview() { + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true); + } + + // Under approval + get canSetToUnderApproval() { + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true); + } + + // Approve + get isReadyForApproval() { + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true); + } + + get canApprove() { + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true); + } + + // Undo approval + get canUndoApproval() { + return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true); + } + delete() { this.loading = true; this._dialogService.openDeleteFilesDialog(null, this._appStateService.activeProject.project.projectId, this.selectedFileIds, () => { @@ -93,42 +117,18 @@ export class ProjectOverviewBulkActionsComponent { this._performBulkAction(this._fileActionService.ocrFile(this.selectedFiles)); } - // Under review - get canSetToUnderReview() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true); - } - setToUnderReview() { this._performBulkAction(this._fileActionService.setFileUnderReview(this.selectedFiles)); } - // Under approval - get canSetToUnderApproval() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true); - } - setToUnderApproval() { this._performBulkAction(this._fileActionService.setFileUnderApproval(this.selectedFiles)); } - // Approve - get isReadyForApproval() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true); - } - - get canApprove() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true); - } - approveDocuments() { this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles)); } - // Undo approval - get canUndoApproval() { - return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true); - } - private _performBulkAction(obs: Observable) { this.loading = true; obs.subscribe().add(() => { diff --git a/apps/red-ui/src/app/modules/projects/components/comments/comments.component.ts b/apps/red-ui/src/app/modules/projects/components/comments/comments.component.ts index 42c78ef49..30a1c421b 100644 --- a/apps/red-ui/src/app/modules/projects/components/comments/comments.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/comments/comments.component.ts @@ -2,11 +2,11 @@ import { ChangeDetectorRef, Component, Input } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Comment } from '@redaction/red-ui-http'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; -import { UserService } from '../../../../services/user.service'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { UserService } from '@services/user.service'; +import { AppStateService } from '@state/app-state.service'; import { TranslateService } from '@ngx-translate/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; @Component({ selector: 'redaction-comments', diff --git a/apps/red-ui/src/app/modules/projects/components/document-info/document-info.component.ts b/apps/red-ui/src/app/modules/projects/components/document-info/document-info.component.ts index 7f187b24a..443ef4c89 100644 --- a/apps/red-ui/src/app/modules/projects/components/document-info/document-info.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/document-info/document-info.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FileAttributesConfig, FileStatus } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; @Component({ @@ -22,11 +22,11 @@ export class DocumentInfoComponent { return this._appStateService.getProjectById(this.file.projectId); } - edit() { - this._dialogService.openDocumentInfoDialog(this.file); - } - get ruleSetName(): string { return this._appStateService.getRuleSetById(this.project.ruleSetId).name; } + + edit() { + this._dialogService.openDocumentInfoDialog(this.file); + } } diff --git a/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts index 70ecca38e..948ea01c7 100644 --- a/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/file-actions/file-actions.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { AppStateService } from '../../../../state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { AppStateService } from '@state/app-state.service'; import { FileActionService } from '../../services/file-action.service'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; @@ -25,6 +25,22 @@ export class FileActionsComponent implements OnInit { private readonly _fileActionService: FileActionService ) {} + get tooltipPosition() { + return this.screen === 'file-preview' ? 'below' : 'above'; + } + + get buttonType() { + return this.screen === 'file-preview' ? 'default' : 'dark-bg'; + } + + get toggleTooltip(): string { + if (!this.permissionsService.isManager()) { + return 'file-preview.toggle-analysis.only-managers'; + } + + return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable'; + } + ngOnInit(): void { if (!this.fileStatus) { this.fileStatus = this.appStateService.activeFile; @@ -43,14 +59,6 @@ export class FileActionsComponent implements OnInit { this.actionPerformed.emit('view-document-info'); } - get tooltipPosition() { - return this.screen === 'file-preview' ? 'below' : 'above'; - } - - get buttonType() { - return this.screen === 'file-preview' ? 'default' : 'dark-bg'; - } - openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) { this._dialogService.openDeleteFilesDialog($event, fileStatusWrapper.projectId, [fileStatusWrapper.fileId], () => { this.actionPerformed.emit('delete'); @@ -110,12 +118,4 @@ export class FileActionsComponent implements OnInit { await this.appStateService.getFiles(); this.actionPerformed.emit(this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis'); } - - get toggleTooltip(): string { - if (!this.permissionsService.isManager()) { - return 'file-preview.toggle-analysis.only-managers'; - } - - return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable'; - } } diff --git a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts index 96cefa40b..d94baac43 100644 --- a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import scrollIntoView from 'scroll-into-view-if-needed'; -import { debounce } from '../../../../utils/debounce'; -import { FileDataModel } from '../../../../models/file/file-data.model'; +import { debounce } from '@utils/debounce'; +import { FileDataModel } from '@models/file/file-data.model'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -17,13 +17,6 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; }) export class FileWorkloadComponent { displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {}; - private _annotations: AnnotationWrapper[]; - - @Input() - set annotations(value: AnnotationWrapper[]) { - this._annotations = value; - } - @Input() selectedAnnotations: AnnotationWrapper[]; @Input() activeViewerPage: number; @Input() shouldDeselectAnnotationsOnPageChange: boolean; @@ -33,22 +26,28 @@ export class FileWorkloadComponent { @Input() fileData: FileDataModel; @Input() hideSkipped: boolean; @Input() annotationActionsTemplate: TemplateRef; - @Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter(); @Output() selectAnnotations = new EventEmitter(); @Output() deselectAnnotations = new EventEmitter(); @Output() selectPage = new EventEmitter(); @Output() toggleSkipped = new EventEmitter(); @Output() annotationsChanged = new EventEmitter(); - quickScrollFirstEnabled = false; quickScrollLastEnabled = false; displayedPages: number[] = []; pagesPanelActive = true; - @ViewChild('annotationsElement') private _annotationsElement: ElementRef; @ViewChild('quickNavigation') private _quickNavigationElement: ElementRef; + constructor(private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _annotationProcessingService: AnnotationProcessingService) {} + + private _annotations: AnnotationWrapper[]; + + @Input() + set annotations(value: AnnotationWrapper[]) { + this._annotations = value; + } + private _multiSelectActive = false; get multiSelectActive(): boolean { @@ -65,8 +64,6 @@ export class FileWorkloadComponent { } } - constructor(private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _annotationProcessingService: AnnotationProcessingService) {} - private get _firstSelectedAnnotation() { return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null; } @@ -218,6 +215,18 @@ export class FileWorkloadComponent { } } + jumpToPreviousWithAnnotations() { + this.selectPage.emit(this._prevPageWithAnnotations()); + } + + jumpToNextWithAnnotations() { + this.selectPage.emit(this._nextPageWithAnnotations()); + } + + _(filter): FilterModel { + return filter as FilterModel; + } + private _selectFirstAnnotationOnCurrentPageIfNecessary() { if ( (!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) && @@ -227,14 +236,6 @@ export class FileWorkloadComponent { } } - jumpToPreviousWithAnnotations() { - this.selectPage.emit(this._prevPageWithAnnotations()); - } - - jumpToNextWithAnnotations() { - this.selectPage.emit(this._nextPageWithAnnotations()); - } - private _navigateAnnotations($event: KeyboardEvent) { if (!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) { const pageIdx = this.displayedPages.indexOf(this.activeViewerPage); @@ -347,8 +348,4 @@ export class FileWorkloadComponent { const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`); FileWorkloadComponent._scrollToFirstElement(elements); } - - _(filter): FilterModel { - return filter as FilterModel; - } } diff --git a/apps/red-ui/src/app/modules/projects/components/needs-work-badge/needs-work-badge.component.ts b/apps/red-ui/src/app/modules/projects/components/needs-work-badge/needs-work-badge.component.ts index 96ab7a372..7fc415b67 100644 --- a/apps/red-ui/src/app/modules/projects/components/needs-work-badge/needs-work-badge.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/needs-work-badge/needs-work-badge.component.ts @@ -1,8 +1,8 @@ import { Component, Input } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; @Component({ selector: 'redaction-needs-work-badge', @@ -14,14 +14,6 @@ export class NeedsWorkBadgeComponent { constructor(private readonly _appStateService: AppStateService, private readonly _permissionsService: PermissionsService) {} - reanalysisRequired() { - if (this.needsWorkInput instanceof ProjectWrapper) { - return this._permissionsService.projectReanalysisRequired(this.needsWorkInput); - } else { - return this._permissionsService.fileRequiresReanalysis(this.needsWorkInput); - } - } - get suggestionColor() { return this._getDictionaryColor('suggestion'); } @@ -58,6 +50,14 @@ export class NeedsWorkBadgeComponent { return this.needsWorkInput instanceof FileStatusWrapper && (this.needsWorkInput).hasAnnotationComments; } + reanalysisRequired() { + if (this.needsWorkInput instanceof ProjectWrapper) { + return this._permissionsService.projectReanalysisRequired(this.needsWorkInput); + } else { + return this._permissionsService.fileRequiresReanalysis(this.needsWorkInput); + } + } + private _getDictionaryColor(type: string) { let ruleSetId = null; if (this.needsWorkInput instanceof ProjectWrapper) { diff --git a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts index 5e7bb8cf0..f7b1e8e65 100644 --- a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts @@ -1,8 +1,8 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { Subscription } from 'rxjs'; @Component({ @@ -29,6 +29,10 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy { private readonly _permissionService: PermissionsService ) {} + get read() { + return this.viewedPages?.pages?.indexOf(this.number) >= 0; + } + ngOnInit(): void { this._subscription = this._appStateService.fileChanged.subscribe(() => { this.canMarkPagesAsViewed = this._permissionService.canMarkPagesAsViewed(); @@ -41,8 +45,20 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy { } } - get read() { - return this.viewedPages?.pages?.indexOf(this.number) >= 0; + toggleReadState() { + if (this.canMarkPagesAsViewed) { + if (this.read) { + this._markPageUnread(); + } else { + this._markPageRead(); + } + } + } + + ngOnDestroy(): void { + if (this._subscription) { + this._subscription.unsubscribe(); + } } private _handlePageRead() { @@ -60,20 +76,6 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy { } } - private _markPageRead() { - this._viewedPagesControllerService - .addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId) - .subscribe(() => { - this.viewedPages?.pages?.push(this.number); - }); - } - - private _markPageUnread() { - this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => { - this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1); - }); - } - // @HostListener('window:keydown', ['$event']) // handleKeyDown(event: KeyboardEvent) { // if (this.canMarkPagesAsViewed) { @@ -91,19 +93,17 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy { // } // } - toggleReadState() { - if (this.canMarkPagesAsViewed) { - if (this.read) { - this._markPageUnread(); - } else { - this._markPageRead(); - } - } + private _markPageRead() { + this._viewedPagesControllerService + .addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId) + .subscribe(() => { + this.viewedPages?.pages?.push(this.number); + }); } - ngOnDestroy(): void { - if (this._subscription) { - this._subscription.unsubscribe(); - } + private _markPageUnread() { + this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => { + this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1); + }); } } diff --git a/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts index fbf2add72..2fa608302 100644 --- a/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts @@ -2,15 +2,15 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, NgZo import { ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http'; import WebViewer, { Annotations, Tools, WebViewerInstance } from '@pdftron/webviewer'; import { TranslateService } from '@ngx-translate/core'; -import { ManualRedactionEntryWrapper } from '../../../../models/file/manual-redaction-entry.wrapper'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; +import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { environment } from '../../../../../environments/environment'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { environment } from '@environments/environment'; import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationActionsService } from '../../services/annotation-actions.service'; -import { UserPreferenceService } from '../../../../services/user-preference.service'; -import { translateQuads } from '../../../../utils/pdf-coordinates'; +import { UserPreferenceService } from '@services/user-preference.service'; +import { translateQuads } from '@utils/pdf-coordinates'; import { BASE_HREF } from '../../../../tokens'; import Tool = Tools.Tool; @@ -20,17 +20,12 @@ import Tool = Tools.Tool; styleUrls: ['./pdf-viewer.component.scss'] }) export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { - private _selectedText = ''; - private _defaultTool: Tool; - private readonly _allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape']; - @Input() fileData: Blob; @Input() fileStatus: FileStatusWrapper; @Input() canPerformActions = false; @Input() annotations: AnnotationWrapper[]; @Input() shouldDeselectAnnotationsOnPageChange = true; @Input() multiSelectActive: boolean; - @Output() fileReady = new EventEmitter(); @Output() annotationSelected = new EventEmitter(); @Output() manualAnnotationRequested = new EventEmitter(); @@ -38,9 +33,11 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { @Output() keyUp = new EventEmitter(); @Output() viewerReady = new EventEmitter(); @Output() annotationsChanged = new EventEmitter(); - @ViewChild('viewer', { static: true }) viewer: ElementRef; instance: WebViewerInstance; + private _selectedText = ''; + private _defaultTool: Tool; + private readonly _allowedKeyboardShortcuts = ['+', '-', 'p', 'r', 'Escape']; constructor( @Inject(BASE_HREF) private readonly _baseHref: string, @@ -71,6 +68,50 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { this._loadViewer(); } + deselectAllAnnotations() { + this.instance.annotManager.deselectAllAnnotations(); + } + + selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) { + let annotations: AnnotationWrapper[]; + let multiSelect: boolean; + if ($event instanceof Array) { + annotations = $event; + multiSelect = false; + } else { + annotations = $event.annotations; + multiSelect = $event.multiSelect; + } + + if (!this.multiSelectActive && !multiSelect) { + this.deselectAllAnnotations(); + } + + const annotationsFromViewer = annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)); + this.instance.annotManager.selectAnnotations(annotationsFromViewer); + this.navigateToPage(annotations[0].pageNumber); + this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]); + } + + deselectAnnotations(annotations: AnnotationWrapper[]) { + this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id))); + } + + navigateToPage(pageNumber: number) { + const activePage = this.instance.docViewer.getCurrentPage(); + if (activePage !== pageNumber) { + this.instance.docViewer.displayPageLocation(pageNumber, 0, 0); + } + } + + setInitialViewerState() { + // viewer init + this.instance.setFitMode('FitPage'); + const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode(); + instanceDisplayMode.mode = 'Single'; + this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); + } + private _loadViewer() { WebViewer( { @@ -394,42 +435,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { }; } - deselectAllAnnotations() { - this.instance.annotManager.deselectAllAnnotations(); - } - - selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) { - let annotations: AnnotationWrapper[]; - let multiSelect: boolean; - if ($event instanceof Array) { - annotations = $event; - multiSelect = false; - } else { - annotations = $event.annotations; - multiSelect = $event.multiSelect; - } - - if (!this.multiSelectActive && !multiSelect) { - this.deselectAllAnnotations(); - } - - const annotationsFromViewer = annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)); - this.instance.annotManager.selectAnnotations(annotationsFromViewer); - this.navigateToPage(annotations[0].pageNumber); - this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]); - } - - deselectAnnotations(annotations: AnnotationWrapper[]) { - this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id))); - } - - navigateToPage(pageNumber: number) { - const activePage = this.instance.docViewer.getCurrentPage(); - if (activePage !== pageNumber) { - this.instance.docViewer.displayPageLocation(pageNumber, 0, 0); - } - } - private _loadDocument() { if (this.fileData) { this.instance.loadDocument(this.fileData, { @@ -446,14 +451,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { }); } - setInitialViewerState() { - // viewer init - this.instance.setFitMode('FitPage'); - const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode(); - instanceDisplayMode.mode = 'Single'; - this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); - } - private _disableHotkeys() { this.instance.hotkeys.off('CTRL+SHIFT+EQUAL'); this.instance.hotkeys.off('COMMAND+SHIFT+EQUAL'); diff --git a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.ts b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.ts index d6b283b99..368cd05aa 100644 --- a/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/project-details/project-details.component.ts @@ -1,12 +1,12 @@ import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; -import { groupBy } from '../../../../utils/functions'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { AppStateService } from '@state/app-state.service'; +import { groupBy } from '@utils/functions'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { Router } from '@angular/router'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { TranslateChartService } from '../../../../services/translate-chart.service'; -import { StatusSorter } from '../../../../utils/sorters/status-sorter'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; +import { PermissionsService } from '@services/permissions.service'; +import { TranslateChartService } from '@services/translate-chart.service'; +import { StatusSorter } from '@utils/sorters/status-sorter'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; @Component({ @@ -30,6 +30,14 @@ export class ProjectDetailsComponent implements OnInit { private readonly _router: Router ) {} + get memberIds(): string[] { + return this.appStateService.activeProject.project.memberIds; + } + + get hasFiles(): boolean { + return this.appStateService.activeProject.hasFiles; + } + ngOnInit(): void { this.calculateChartConfig(); this.appStateService.fileChanged.subscribe(() => { @@ -37,10 +45,6 @@ export class ProjectDetailsComponent implements OnInit { }); } - get memberIds(): string[] { - return this.appStateService.activeProject.project.memberIds; - } - calculateChartConfig(): void { if (this.appStateService.activeProject) { const groups = groupBy(this.appStateService.activeProject?.files, 'status'); @@ -54,10 +58,6 @@ export class ProjectDetailsComponent implements OnInit { } } - get hasFiles(): boolean { - return this.appStateService.activeProject.hasFiles; - } - toggleFilter(filterType: 'needsWorkFilters' | 'statusFilters', key: string): void { const filter = this.filters[filterType].find((f) => f.key === key); filter.checked = !filter.checked; diff --git a/apps/red-ui/src/app/modules/projects/components/project-listing-actions/project-listing-actions.component.ts b/apps/red-ui/src/app/modules/projects/components/project-listing-actions/project-listing-actions.component.ts index d4df0c857..1dfa5f355 100644 --- a/apps/red-ui/src/app/modules/projects/components/project-listing-actions/project-listing-actions.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/project-listing-actions/project-listing-actions.component.ts @@ -1,11 +1,11 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; -import { StatusSorter } from '../../../../utils/sorters/status-sorter'; -import { download } from '../../../../utils/file-download-utils'; -import { computerize } from '../../../../utils/functions'; +import { PermissionsService } from '@services/permissions.service'; +import { ProjectWrapper } from '@state/model/project.wrapper'; +import { StatusSorter } from '@utils/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 { AppStateService } from '@state/app-state.service'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; @Component({ diff --git a/apps/red-ui/src/app/modules/projects/components/project-listing-details/project-listing-details.component.ts b/apps/red-ui/src/app/modules/projects/components/project-listing-details/project-listing-details.component.ts index 145e02594..3feac5296 100644 --- a/apps/red-ui/src/app/modules/projects/components/project-listing-details/project-listing-details.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/project-listing-details/project-listing-details.component.ts @@ -1,7 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { AppStateService } from '../../../../state/app-state.service'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { AppStateService } from '@state/app-state.service'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; @Component({ selector: 'redaction-project-listing-details', diff --git a/apps/red-ui/src/app/modules/projects/components/team-members/team-members.component.ts b/apps/red-ui/src/app/modules/projects/components/team-members/team-members.component.ts index 4fc9a8cbe..8648cc76a 100644 --- a/apps/red-ui/src/app/modules/projects/components/team-members/team-members.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/team-members/team-members.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; @Component({ selector: 'redaction-team-members', @@ -30,14 +30,14 @@ export class TeamMembersComponent { return this.expandedTeam || !this.overflowCount ? this.memberIds : this.memberIds.slice(0, this.maxTeamMembersBeforeExpand - 1); } - toggleExpandedTeam() { - this.expandedTeam = !this.expandedTeam; - } - get overflowCount() { return this.memberIds.length > this.maxTeamMembersBeforeExpand ? this.memberIds.length - (this.maxTeamMembersBeforeExpand - 1) : 0; } + toggleExpandedTeam() { + this.expandedTeam = !this.expandedTeam; + } + canRemoveMember(userId: string) { return this.canRemove && this.unremovableMembers.indexOf(userId) === -1; } diff --git a/apps/red-ui/src/app/modules/projects/components/type-annotation-icon/type-annotation-icon.component.ts b/apps/red-ui/src/app/modules/projects/components/type-annotation-icon/type-annotation-icon.component.ts index 022f29f91..7a3f36068 100644 --- a/apps/red-ui/src/app/modules/projects/components/type-annotation-icon/type-annotation-icon.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/type-annotation-icon/type-annotation-icon.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { AppStateService } from '@state/app-state.service'; @Component({ selector: 'redaction-type-annotation-icon', diff --git a/apps/red-ui/src/app/modules/projects/components/type-filter/type-filter.component.ts b/apps/red-ui/src/app/modules/projects/components/type-filter/type-filter.component.ts index dc3e7dc4e..b733c0211 100644 --- a/apps/red-ui/src/app/modules/projects/components/type-filter/type-filter.component.ts +++ b/apps/red-ui/src/app/modules/projects/components/type-filter/type-filter.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; -import { AppStateService } from '../../../../state/app-state.service'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; +import { AppStateService } from '@state/app-state.service'; @Component({ selector: 'redaction-type-filter', @@ -22,11 +22,12 @@ export class TypeFilterComponent implements OnInit { ]; private _needsAnalysisKeys = ['add-dictionary', 'remove-dictionary', 'remove-only-here', 'pending-analysis', 'analysis']; - isSuggestion = (key: string) => this._suggestionsKeys.includes(key); - needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key); - constructor(private readonly _appStateService: AppStateService) {} + isSuggestion = (key: string) => this._suggestionsKeys.includes(key); + + needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key); + ngOnInit(): void { this.dictionaryColor = this._appStateService.getDictionaryColor(this.filter.key); } diff --git a/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.ts index 56a7552ca..c9faa687e 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/add-edit-project-dialog/add-edit-project-dialog.component.ts @@ -2,8 +2,8 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Project, RuleSetModel } from '@redaction/red-ui-http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AppStateService } from '../../../../state/app-state.service'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; +import { AppStateService } from '@state/app-state.service'; +import { ProjectWrapper } from '@state/model/project.wrapper'; import * as moment from 'moment'; @Component({ @@ -44,17 +44,6 @@ export class AddEditProjectDialogComponent { return this.projectForm.controls['downloadFileTypes']?.value?.length ? this.projectForm.controls['downloadFileTypes'].value.length : 0; } - private _filterInvalidRuleSets() { - this.ruleSets = this._appStateService.ruleSets.filter((r) => { - if (this.project?.ruleSetId === r.ruleSetId) { - return true; - } - const notYetValid = !!r.validFrom && moment(r.validFrom).isAfter(moment()); - const notValidAnymore = !!r.validTo && moment(r.validTo).add(1, 'd').isBefore(moment()); - return !(notYetValid || notValidAnymore); - }); - } - get changed() { if (!this.project) { return true; @@ -103,17 +92,6 @@ export class AddEditProjectDialogComponent { } } - private _formToObject(): Project { - return { - projectName: this.projectForm.get('projectName').value, - description: this.projectForm.get('description').value, - dueDate: this.hasDueDate ? this.projectForm.get('dueDate').value : undefined, - ruleSetId: this.projectForm.get('ruleSetId').value, - downloadFileTypes: this.projectForm.get('downloadFileTypes').value, - reportTypes: this.projectForm.get('reportTypes').value - }; - } - async saveProjectAndAddMembers() { const project: Project = this._formToObject(); project.projectId = this.project?.projectId; @@ -140,4 +118,26 @@ export class AddEditProjectDialogComponent { } } } + + private _filterInvalidRuleSets() { + this.ruleSets = this._appStateService.ruleSets.filter((r) => { + if (this.project?.ruleSetId === r.ruleSetId) { + return true; + } + const notYetValid = !!r.validFrom && moment(r.validFrom).isAfter(moment()); + const notValidAnymore = !!r.validTo && moment(r.validTo).add(1, 'd').isBefore(moment()); + return !(notYetValid || notValidAnymore); + }); + } + + private _formToObject(): Project { + return { + projectName: this.projectForm.get('projectName').value, + description: this.projectForm.get('description').value, + dueDate: this.hasDueDate ? this.projectForm.get('dueDate').value : undefined, + ruleSetId: this.projectForm.get('ruleSetId').value, + downloadFileTypes: this.projectForm.get('downloadFileTypes').value, + reportTypes: this.projectForm.get('reportTypes').value + }; + } } diff --git a/apps/red-ui/src/app/modules/projects/dialogs/assign-owner-dialog/assign-owner-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/assign-owner-dialog/assign-owner-dialog.component.ts index f5540b17c..e69db6b4c 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/assign-owner-dialog/assign-owner-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/assign-owner-dialog/assign-owner-dialog.component.ts @@ -1,12 +1,12 @@ import { Component, Inject } from '@angular/core'; import { ProjectControllerService, StatusControllerService } from '@redaction/red-ui-http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { AppStateService } from '../../../../state/app-state.service'; -import { UserService } from '../../../../services/user.service'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; +import { AppStateService } from '@state/app-state.service'; +import { UserService } from '@services/user.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; class DialogData { type: 'file' | 'project'; @@ -37,39 +37,6 @@ export class AssignOwnerDialogComponent { this._loadData(); } - private _loadData() { - if (this.data.type === 'project') { - const project = this.data.project; - this.usersForm = this._formBuilder.group({ - singleUser: [project?.ownerId, Validators.required], - approvers: [[...project?.approverIds]], - members: [[...project?.memberIds]] - }); - this.searchForm = this._formBuilder.group({ - query: [''] - }); - this.usersForm.get('singleUser').valueChanges.subscribe((singleUser) => { - if (!this.isApprover(singleUser)) { - this.toggleApprover(singleUser); - } - // If it is an approver, it is already a member, no need to check - }); - } - - if (this.data.type === 'file') { - const uniqueReviewers = new Set(); - for (const file of this.data.files) { - if (file.currentReviewer) { - uniqueReviewers.add(file.currentReviewer); - } - } - const singleUser = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.userId; - this.usersForm = this._formBuilder.group({ - singleUser: [singleUser] - }); - } - } - get selectedSingleUser(): string { return this.usersForm.get('singleUser').value; } @@ -86,6 +53,49 @@ export class AssignOwnerDialogComponent { return this.usersForm.get('members').value; } + get singleUsersSelectOptions() { + return this.data.type === 'file' ? this._appStateService.activeProject.memberIds : this.userService.managerUsers.map((m) => m.userId); + } + + get multiUsersSelectOptions() { + const searchQuery = this.searchForm.get('query').value; + return this.userService.eligibleUsers + .filter((user) => this.userService.getNameForId(user.userId).toLowerCase().includes(searchQuery.toLowerCase())) + .map((user) => user.userId); + } + + get changed(): boolean { + if (this.data.ignoreChanged) { + return true; + } + + if (this.data.type === 'project') { + if (this.data.project.ownerId !== this.selectedSingleUser) { + return true; + } + + const initialMembers = this.data.project.memberIds.sort(); + const currentMembers = this.selectedUsersList.sort(); + + const initialApprovers = this.data.project.approverIds.sort(); + const currentApprovers = this.selectedApproversList.sort(); + + if (this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers)) { + return true; + } + } else if (this.data.type === 'file') { + const reviewerId = this.selectedSingleUser; + + for (const file of this.data.files) { + if (file.currentReviewer !== reviewerId) { + return true; + } + } + } + + return false; + } + isOwner(userId: string): boolean { return userId === this.selectedSingleUser; } @@ -127,17 +137,6 @@ export class AssignOwnerDialogComponent { this.dialogRef.close(result); } - get singleUsersSelectOptions() { - return this.data.type === 'file' ? this._appStateService.activeProject.memberIds : this.userService.managerUsers.map((m) => m.userId); - } - - get multiUsersSelectOptions() { - const searchQuery = this.searchForm.get('query').value; - return this.userService.eligibleUsers - .filter((user) => this.userService.getNameForId(user.userId).toLowerCase().includes(searchQuery.toLowerCase())) - .map((user) => user.userId); - } - isMemberSelected(userId: string): boolean { return this.selectedUsersList.indexOf(userId) !== -1; } @@ -175,6 +174,39 @@ export class AssignOwnerDialogComponent { } } + private _loadData() { + if (this.data.type === 'project') { + const project = this.data.project; + this.usersForm = this._formBuilder.group({ + singleUser: [project?.ownerId, Validators.required], + approvers: [[...project?.approverIds]], + members: [[...project?.memberIds]] + }); + this.searchForm = this._formBuilder.group({ + query: [''] + }); + this.usersForm.get('singleUser').valueChanges.subscribe((singleUser) => { + if (!this.isApprover(singleUser)) { + this.toggleApprover(singleUser); + } + // If it is an approver, it is already a member, no need to check + }); + } + + if (this.data.type === 'file') { + const uniqueReviewers = new Set(); + for (const file of this.data.files) { + if (file.currentReviewer) { + uniqueReviewers.add(file.currentReviewer); + } + } + const singleUser = uniqueReviewers.size === 1 ? uniqueReviewers.values().next().value : this.userService.userId; + this.usersForm = this._formBuilder.group({ + singleUser: [singleUser] + }); + } + } + private _compareLists(l1: string[], l2: string[]) { if (l1.length !== l2.length) { return true; @@ -188,36 +220,4 @@ export class AssignOwnerDialogComponent { return false; } - - get changed(): boolean { - if (this.data.ignoreChanged) { - return true; - } - - if (this.data.type === 'project') { - if (this.data.project.ownerId !== this.selectedSingleUser) { - return true; - } - - const initialMembers = this.data.project.memberIds.sort(); - const currentMembers = this.selectedUsersList.sort(); - - const initialApprovers = this.data.project.approverIds.sort(); - const currentApprovers = this.selectedApproversList.sort(); - - if (this._compareLists(initialMembers, currentMembers) || this._compareLists(initialApprovers, currentApprovers)) { - return true; - } - } else if (this.data.type === 'file') { - const reviewerId = this.selectedSingleUser; - - for (const file of this.data.files) { - if (file.currentReviewer !== reviewerId) { - return true; - } - } - } - - return false; - } } diff --git a/apps/red-ui/src/app/modules/projects/dialogs/document-info-dialog/document-info-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/document-info-dialog/document-info-dialog.component.ts index 1bb4b16f1..7f60d54f8 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/document-info-dialog/document-info-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/document-info-dialog/document-info-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { FileAttributeConfig, FileAttributesControllerService, FileStatus } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; @Component({ selector: 'redaction-document-info-dialog', @@ -32,7 +32,13 @@ export class DocumentInfoDialogComponent implements OnInit { this.attributes = (await this._fileAttributesService.getFileAttributesConfiguration(this._project.ruleSetId).toPromise()).fileAttributeConfigs.filter( (attr) => attr.editable ); - const formConfig = this.attributes.reduce((acc, attr) => ({ ...acc, [attr.id]: [this.file.fileAttributes?.attributeIdToValue[attr.id]] }), {}); + const formConfig = this.attributes.reduce( + (acc, attr) => ({ + ...acc, + [attr.id]: [this.file.fileAttributes?.attributeIdToValue[attr.id]] + }), + {} + ); this.documentInfoForm = this._formBuilder.group(formConfig); } diff --git a/apps/red-ui/src/app/modules/projects/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts index 1d0018dd9..70e9cd5e7 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts @@ -1,13 +1,13 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { MatDialogRef } from '@angular/material/dialog'; import { ForceRedactionRequest, LegalBasisMappingControllerService } from '@redaction/red-ui-http'; -import { NotificationService } from '../../../../services/notification.service'; +import { NotificationService } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; -import { UserService } from '../../../../services/user.service'; +import { UserService } from '@services/user.service'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; export interface LegalBasisOption { label?: string; diff --git a/apps/red-ui/src/app/modules/projects/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index b9864c2f2..903d6b503 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -1,15 +1,15 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AddRedactionRequest, LegalBasisMappingControllerService, TypeValue } from '@redaction/red-ui-http'; -import { NotificationService } from '../../../../services/notification.service'; +import { NotificationService } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; -import { UserService } from '../../../../services/user.service'; -import { ManualRedactionEntryWrapper } from '../../../../models/file/manual-redaction-entry.wrapper'; +import { UserService } from '@services/user.service'; +import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; -import { ManualAnnotationResponse } from '../../../../models/file/manual-annotation-response'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { ManualAnnotationResponse } from '@models/file/manual-annotation-response'; +import { PermissionsService } from '@services/permissions.service'; export interface LegalBasisOption { label?: string; @@ -32,10 +32,6 @@ export class ManualAnnotationDialogComponent implements OnInit { redactionDictionaries: TypeValue[] = []; legalOptions: LegalBasisOption[] = []; - get title() { - return this._manualAnnotationService.getTitle(this.manualRedactionEntryWrapper.type); - } - constructor( private readonly _appStateService: AppStateService, private readonly _userService: UserService, @@ -49,6 +45,18 @@ export class ManualAnnotationDialogComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper ) {} + get title() { + return this._manualAnnotationService.getTitle(this.manualRedactionEntryWrapper.type); + } + + get displayedDictionaryLabel() { + const dictType = this.redactionForm.get('dictionary').value; + if (dictType) { + return this.redactionDictionaries.find((d) => d.type === dictType).label; + } + return null; + } + async ngOnInit() { this._legalBasisMappingControllerService.getLegalBasisMapping(this._appStateService.activeProject.ruleSetId).subscribe((data) => { data.map((lbm) => { @@ -84,14 +92,6 @@ export class ManualAnnotationDialogComponent implements OnInit { this.redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label)); } - get displayedDictionaryLabel() { - const dictType = this.redactionForm.get('dictionary').value; - if (dictType) { - return this.redactionDictionaries.find((d) => d.type === dictType).label; - } - return null; - } - handleAddRedaction() { this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry); this._manualAnnotationService.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry).subscribe( diff --git a/apps/red-ui/src/app/modules/projects/dialogs/remove-annotations-dialog/remove-annotations-dialog.component.ts b/apps/red-ui/src/app/modules/projects/dialogs/remove-annotations-dialog/remove-annotations-dialog.component.ts index ed996a11f..8917354f3 100644 --- a/apps/red-ui/src/app/modules/projects/dialogs/remove-annotations-dialog/remove-annotations-dialog.component.ts +++ b/apps/red-ui/src/app/modules/projects/dialogs/remove-annotations-dialog/remove-annotations-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { TranslateService } from '@ngx-translate/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; diff --git a/apps/red-ui/src/app/modules/projects/projects-routing.module.ts b/apps/red-ui/src/app/modules/projects/projects-routing.module.ts index eb656c35a..1eac40c95 100644 --- a/apps/red-ui/src/app/modules/projects/projects-routing.module.ts +++ b/apps/red-ui/src/app/modules/projects/projects-routing.module.ts @@ -1,10 +1,10 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ProjectListingScreenComponent } from './screens/project-listing-screen/project-listing-screen.component'; -import { CompositeRouteGuard } from '../../guards/composite-route.guard'; +import { CompositeRouteGuard } from '@guards/composite-route.guard'; import { AuthGuard } from '../auth/auth.guard'; import { RedRoleGuard } from '../auth/red-role.guard'; -import { AppStateGuard } from '../../state/app-state.guard'; +import { AppStateGuard } from '@state/app-state.guard'; import { ProjectOverviewScreenComponent } from './screens/project-overview-screen/project-overview-screen.component'; import { FilePreviewScreenComponent } from './screens/file-preview-screen/file-preview-screen.component'; diff --git a/apps/red-ui/src/app/modules/projects/projects.module.ts b/apps/red-ui/src/app/modules/projects/projects.module.ts index 81ebdb3a4..f18e12ba9 100644 --- a/apps/red-ui/src/app/modules/projects/projects.module.ts +++ b/apps/red-ui/src/app/modules/projects/projects.module.ts @@ -24,9 +24,9 @@ import { TeamMembersComponent } from './components/team-members/team-members.com import { ProjectListingActionsComponent } from './components/project-listing-actions/project-listing-actions.component'; import { DocumentInfoComponent } from './components/document-info/document-info.component'; import { FileWorkloadComponent } from './components/file-workload/file-workload.component'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@shared/shared.module'; import { ProjectsRoutingModule } from './projects-routing.module'; -import { FileUploadDownloadModule } from '../upload-download/file-upload-download.module'; +import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module'; import { ProjectsDialogService } from './services/projects-dialog.service'; import { AnnotationActionsService } from './services/annotation-actions.service'; import { FileActionService } from './services/file-action.service'; diff --git a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts index df81cd6ce..cbcd81e74 100644 --- a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts @@ -1,34 +1,34 @@ import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { PdfViewerComponent } from '../../components/pdf-viewer/pdf-viewer.component'; -import { debounce } from '../../../../utils/debounce'; +import { debounce } from '@utils/debounce'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; -import { ManualRedactionEntryWrapper } from '../../../../models/file/manual-redaction-entry.wrapper'; -import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper'; -import { ManualAnnotationResponse } from '../../../../models/file/manual-annotation-response'; -import { AnnotationData, FileDataModel } from '../../../../models/file/file-data.model'; +import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { ManualAnnotationResponse } from '@models/file/manual-annotation-response'; +import { AnnotationData, FileDataModel } from '@models/file/file-data.model'; import { FileActionService } from '../../services/file-action.service'; import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; import { tap } from 'rxjs/operators'; -import { NotificationService } from '../../../../services/notification.service'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { PermissionsService } from '../../../../services/permissions.service'; +import { NotificationService } from '@services/notification.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { PermissionsService } from '@services/permissions.service'; import { Subscription, timer } from 'rxjs'; -import { handleFilterDelta, processFilters } from '../../../shared/components/filter/utils/filter-utils'; -import { UserPreferenceService } from '../../../../services/user-preference.service'; -import { UserService } from '../../../../services/user.service'; +import { handleFilterDelta, processFilters } from '@shared/components/filter/utils/filter-utils'; +import { UserPreferenceService } from '@services/user-preference.service'; +import { UserService } from '@services/user.service'; import { FormBuilder, FormGroup } from '@angular/forms'; import { FileManagementControllerService, StatusControllerService } from '@redaction/red-ui-http'; import { PdfViewerDataService } from '../../services/pdf-viewer-data.service'; -import { download } from '../../../../utils/file-download-utils'; -import { ViewMode } from '../../../../models/file/view-mode'; +import { download } from '@utils/file-download-utils'; +import { ViewMode } from '@models/file/view-mode'; import { FileWorkloadComponent } from '../../components/file-workload/file-workload.component'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; -import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strategy'; +import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; @@ -60,12 +60,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, hideSkipped = false; displayPDFViewer = false; viewDocumentInfo = false; + @ViewChild(FileWorkloadComponent) fileWorkloadComponent: FileWorkloadComponent; private _instance: WebViewerInstance; private _lastPage: string; - @ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent; @ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent; - @ViewChild(FileWorkloadComponent) fileWorkloadComponent: FileWorkloadComponent; constructor( readonly appStateService: AppStateService, diff --git a/apps/red-ui/src/app/modules/projects/screens/project-listing-screen/project-listing-screen.component.ts b/apps/red-ui/src/app/modules/projects/screens/project-listing-screen/project-listing-screen.component.ts index eb1a9fc72..b70598eb4 100644 --- a/apps/red-ui/src/app/modules/projects/screens/project-listing-screen/project-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/projects/screens/project-listing-screen/project-listing-screen.component.ts @@ -1,31 +1,31 @@ import { Component, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Project, RuleSetModel } from '@redaction/red-ui-http'; -import { AppStateService } from '../../../../state/app-state.service'; -import { UserService } from '../../../../services/user.service'; -import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; -import { groupBy } from '../../../../utils/functions'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; +import { Project, RuleSetModel, User } from '@redaction/red-ui-http'; +import { AppStateService } from '@state/app-state.service'; +import { UserService } from '@services/user.service'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { groupBy } from '@utils/functions'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; import { annotationFilterChecker, processFilters, projectMemberChecker, projectStatusChecker, ruleSetChecker -} from '../../../shared/components/filter/utils/filter-utils'; +} from '@shared/components/filter/utils/filter-utils'; import { TranslateService } from '@ngx-translate/core'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; +import { PermissionsService } from '@services/permissions.service'; +import { ProjectWrapper } from '@state/model/project.wrapper'; import { Subscription, timer } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; -import { TranslateChartService } from '../../../../services/translate-chart.service'; -import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter'; -import { StatusSorter } from '../../../../utils/sorters/status-sorter'; +import { TranslateChartService } from '@services/translate-chart.service'; +import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter'; +import { StatusSorter } from '@utils/sorters/status-sorter'; import { NavigationEnd, NavigationStart, Router } from '@angular/router'; -import { FilterComponent } from '../../../shared/components/filter/filter.component'; +import { FilterComponent } from '@shared/components/filter/filter.component'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; -import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strategy'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; +import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; @Component({ selector: 'redaction-project-listing-screen', @@ -33,23 +33,19 @@ import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strateg styleUrls: ['./project-listing-screen.component.scss'] }) export class ProjectListingScreenComponent extends BaseListingComponent implements OnInit, OnDestroy, OnAttach, OnDetach { - protected readonly _searchKey = 'name'; - protected readonly _sortKey = 'project-listing'; - projectsChartData: DoughnutChartConfig[] = []; documentsChartData: DoughnutChartConfig[] = []; - statusFilters: FilterModel[]; peopleFilters: FilterModel[]; needsWorkFilters: FilterModel[]; ruleSetFilters: FilterModel[]; - detailsContainerFilters: { statusFilters: FilterModel[]; } = { statusFilters: [] }; - + protected readonly _searchKey = 'name'; + protected readonly _sortKey = 'project-listing'; private _projectAutoUpdateTimer: Subscription; private _lastScrollPosition: number; @@ -78,6 +74,40 @@ export class ProjectListingScreenComponent extends BaseListingComponent p.project.status === Project.StatusEnum.ACTIVE).length; + } + + get inactiveProjectsCount() { + return this.allEntities.length - this.activeProjectsCount; + } + + protected get _filterComponents(): FilterComponent[] { + return [this._statusFilterComponent, this._peopleFilterComponent, this._needsWorkFilterComponent, this._ruleSetFilterComponent]; + } + + protected get _filters(): { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[] { + return [ + { values: this.statusFilters, checker: projectStatusChecker }, + { values: this.peopleFilters, checker: projectMemberChecker }, + { + values: this.needsWorkFilters, + checker: annotationFilterChecker, + matchAll: true, + checkerArgs: this.permissionsService + }, + { values: this.ruleSetFilters, checker: ruleSetChecker } + ]; + } + ngOnInit(): void { this._calculateData(); @@ -120,49 +150,8 @@ export class ProjectListingScreenComponent extends BaseListingComponent StatusSorter[a.key] - StatusSorter[b.key]); - this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData); - } - - get user() { - return this._userService.user; - } - - get activeProjectsCount() { - return this.allEntities.filter((p) => p.project.status === Project.StatusEnum.ACTIVE).length; - } - - get inactiveProjectsCount() { - return this.allEntities.length - this.activeProjectsCount; + getUser(id: string): User { + return this._userService.getUserById(id); } documentCount(project: ProjectWrapper) { @@ -195,6 +184,41 @@ export class ProjectListingScreenComponent extends BaseListingComponent ({ ...f })) + }; + } + + private _loadEntitiesFromState() { + this.allEntities = this._appStateService.allProjects; + } + + private _calculateData() { + this._computeAllFilters(); + this._filterEntities(); + this.projectsChartData = [ + { value: this.activeProjectsCount, color: 'ACTIVE', label: 'active' }, + { value: this.inactiveProjectsCount, color: 'DELETED', label: 'archived' } + ]; + const groups = groupBy(this._appStateService.aggregatedFiles, 'status'); + this.documentsChartData = []; + for (const key of Object.keys(groups)) { + this.documentsChartData.push({ + value: groups[key].length, + color: key, + label: key, + key: key + }); + } + this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]); + this.documentsChartData = this._translateChartService.translateStatus(this.documentsChartData); + } + private _computeAllFilters() { const allDistinctFileStatus = new Set(); const allDistinctPeople = new Set(); @@ -260,28 +284,4 @@ export class ProjectListingScreenComponent extends BaseListingComponent ({ ...f })) - }; - } - - actionPerformed() { - this._calculateData(); - } } diff --git a/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.ts b/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.ts index e11a02959..66022526a 100644 --- a/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/projects/screens/project-overview-screen/project-overview-screen.component.ts @@ -1,31 +1,31 @@ import { Component, HostListener, Injector, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { NavigationEnd, NavigationStart, Router } from '@angular/router'; -import { NotificationService, NotificationType } from '../../../../services/notification.service'; -import { AppStateService } from '../../../../state/app-state.service'; -import { FileDropOverlayService } from '../../../upload-download/services/file-drop-overlay.service'; -import { FileUploadModel } from '../../../upload-download/model/file-upload.model'; -import { FileUploadService } from '../../../upload-download/services/file-upload.service'; -import { StatusOverlayService } from '../../../upload-download/services/status-overlay.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; +import { AppStateService } from '@state/app-state.service'; +import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service'; +import { FileUploadModel } from '@upload-download/model/file-upload.model'; +import { FileUploadService } from '@upload-download/services/file-upload.service'; +import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; import { TranslateService } from '@ngx-translate/core'; -import { FilterModel } from '../../../shared/components/filter/model/filter.model'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; import * as moment from 'moment'; import { ProjectDetailsComponent } from '../../components/project-details/project-details.component'; -import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; -import { annotationFilterChecker, keyChecker, processFilters } from '../../../shared/components/filter/utils/filter-utils'; -import { PermissionsService } from '../../../../services/permissions.service'; -import { UserService } from '../../../../services/user.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { annotationFilterChecker, keyChecker, processFilters } from '@shared/components/filter/utils/filter-utils'; +import { PermissionsService } from '@services/permissions.service'; +import { UserService } from '@services/user.service'; import { FileStatus } from '@redaction/red-ui-http'; import { Subscription, timer } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; -import { RedactionFilterSorter } from '../../../../utils/sorters/redaction-filter-sorter'; -import { StatusSorter } from '../../../../utils/sorters/status-sorter'; -import { convertFiles, handleFileDrop } from '../../../../utils/file-drop-utils'; -import { FilterComponent } from '../../../shared/components/filter/filter.component'; +import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter'; +import { StatusSorter } from '@utils/sorters/status-sorter'; +import { convertFiles, handleFileDrop } from '@utils/file-drop-utils'; +import { FilterComponent } from '@shared/components/filter/filter.component'; import { ProjectsDialogService } from '../../services/projects-dialog.service'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; -import { BaseListingComponent } from '../../../shared/base/base-listing.component'; -import { ProjectWrapper } from '../../../../state/model/project.wrapper'; -import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strategy'; +import { BaseListingComponent } from '@shared/base/base-listing.component'; +import { ProjectWrapper } from '@state/model/project.wrapper'; +import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy'; @Component({ selector: 'redaction-project-overview-screen', @@ -33,20 +33,17 @@ import { OnAttach, OnDetach } from '../../../../utils/custom-route-reuse.strateg styleUrls: ['./project-overview-screen.component.scss'] }) export class ProjectOverviewScreenComponent extends BaseListingComponent implements OnInit, OnDestroy, OnDetach, OnAttach { - protected readonly _searchKey = 'searchField'; - protected readonly _selectionKey = 'fileId'; - protected readonly _sortKey = 'project-overview'; - statusFilters: FilterModel[]; peopleFilters: FilterModel[]; needsWorkFilters: FilterModel[]; collapsedDetails = false; - detailsContainerFilters: { needsWorkFilters: FilterModel[]; statusFilters: FilterModel[]; } = { needsWorkFilters: [], statusFilters: [] }; - + protected readonly _searchKey = 'searchField'; + protected readonly _selectionKey = 'fileId'; + protected readonly _sortKey = 'project-overview'; @ViewChild('projectDetailsComponent', { static: false }) private _projectDetailsComponent: ProjectDetailsComponent; private _filesAutoUpdateTimer: Subscription; @@ -77,6 +74,27 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent { this.calculateData(); @@ -201,6 +207,46 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent { + this._router.navigate(['/main/projects']); + }); + } + + openAssignProjectMembersDialog(): void { + this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.activeProject, () => { + this.reloadProjects(); + }); + } + + toggleCollapsedDetails() { + this.collapsedDetails = !this.collapsedDetails; + } + + protected _preFilter() { + this.detailsContainerFilters = { + needsWorkFilters: this.needsWorkFilters.map((f) => ({ ...f })), + statusFilters: this.statusFilters.map((f) => ({ ...f })) + }; + } + + private _loadEntitiesFromState() { + if (this.activeProject) this.allEntities = this.activeProject.files; + } + private async _uploadFiles(files: FileUploadModel[]) { const fileCount = await this._fileUploadService.uploadFiles(files); if (fileCount) { @@ -277,53 +323,4 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent RedactionFilterSorter[a.key] - RedactionFilterSorter[b.key]); this.needsWorkFilters = processFilters(this.needsWorkFilters, needsWorkFilters); } - - fileLink(fileStatus: FileStatusWrapper) { - return this.permissionsService.canOpenFile(fileStatus) ? ['/main/projects/' + this.activeProject.project.projectId + '/file/' + fileStatus.fileId] : []; - } - - protected get _filters(): { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[] { - return [ - { values: this.statusFilters, checker: keyChecker('status') }, - { values: this.peopleFilters, checker: keyChecker('currentReviewer') }, - { - values: this.needsWorkFilters, - checker: annotationFilterChecker, - matchAll: true, - checkerArgs: this.permissionsService - } - ]; - } - - protected _preFilter() { - this.detailsContainerFilters = { - needsWorkFilters: this.needsWorkFilters.map((f) => ({ ...f })), - statusFilters: this.statusFilters.map((f) => ({ ...f })) - }; - } - - bulkActionPerformed() { - this.selectedEntitiesIds = []; - this.reloadProjects(); - } - - openEditProjectDialog($event: MouseEvent) { - this._dialogService.openEditProjectDialog($event, this.activeProject); - } - - openDeleteProjectDialog($event: MouseEvent) { - this._dialogService.openDeleteProjectDialog($event, this.activeProject, () => { - this._router.navigate(['/main/projects']); - }); - } - - openAssignProjectMembersDialog(): void { - this._dialogService.openAssignProjectMembersAndOwnerDialog(null, this.activeProject, () => { - this.reloadProjects(); - }); - } - - toggleCollapsedDetails() { - this.collapsedDetails = !this.collapsedDetails; - } } diff --git a/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts index 559dccea9..1308837f7 100644 --- a/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts @@ -1,12 +1,12 @@ import { EventEmitter, Inject, Injectable, NgZone } from '@angular/core'; -import { PermissionsService } from '../../../services/permissions.service'; +import { PermissionsService } from '@services/permissions.service'; import { ManualAnnotationService } from './manual-annotation.service'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { Observable } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; import { AddRedactionRequest } from '@redaction/red-ui-http'; -import { getFirstRelevantTextPart } from '../../../utils/functions'; -import { AnnotationPermissions } from '../../../models/file/annotation.permissions'; +import { getFirstRelevantTextPart } from '@utils/functions'; +import { AnnotationPermissions } from '@models/file/annotation.permissions'; import { ProjectsDialogService } from './projects-dialog.service'; import { BASE_HREF } from '../../../tokens'; @@ -90,17 +90,6 @@ export class AnnotationActionsService { }); } - private _processObsAndEmit(obs: Observable, annotation: AnnotationWrapper, annotationsChanged: EventEmitter) { - obs.subscribe( - () => { - annotationsChanged.emit(annotation); - }, - () => { - annotationsChanged.emit(); - } - ); - } - getViewerAvailableActions(annotations: AnnotationWrapper[], annotationsChanged: EventEmitter): Record[] { const availableActions = []; @@ -233,6 +222,17 @@ export class AnnotationActionsService { return availableActions; } + private _processObsAndEmit(obs: Observable, annotation: AnnotationWrapper, annotationsChanged: EventEmitter) { + obs.subscribe( + () => { + annotationsChanged.emit(annotation); + }, + () => { + annotationsChanged.emit(); + } + ); + } + private _getFalsePositiveText(annotation: AnnotationWrapper) { if (annotation.canBeMarkedAsFalsePositive) { let text; diff --git a/apps/red-ui/src/app/modules/projects/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/projects/services/annotation-draw.service.ts index 87c3b2215..a538707c0 100644 --- a/apps/red-ui/src/app/modules/projects/services/annotation-draw.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/annotation-draw.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@angular/core'; import { Annotations, WebViewerInstance } from '@pdftron/webviewer'; import { Rectangle, RedactionLogControllerService, SectionGrid, SectionRectangle } from '@redaction/red-ui-http'; -import { hexToRgb } from '../../../utils/functions'; -import { AppStateService } from '../../../state/app-state.service'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; -import { UserPreferenceService } from '../../../services/user-preference.service'; +import { hexToRgb } from '@utils/functions'; +import { AppStateService } from '@state/app-state.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { UserPreferenceService } from '@services/user-preference.service'; @Injectable() export class AnnotationDrawService { @@ -112,6 +112,22 @@ export class AnnotationDrawService { return new activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b); } + annotationToQuads(annotation: Annotations.Annotation, activeViewer: WebViewerInstance) { + const x1 = annotation.getRect().x1; + const y1 = annotation.getRect().y1 + annotation.getRect().getHeight(); + + const x2 = annotation.getRect().x1 + annotation.getRect().getWidth(); + const y2 = annotation.getRect().y1 + annotation.getRect().getHeight(); + + const x3 = annotation.getRect().x1 + annotation.getRect().getWidth(); + const y3 = annotation.getRect().y1; + + const x4 = annotation.getRect().x1; + const y4 = annotation.getRect().y1; + + return new activeViewer.CoreControls.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4); + } + private _rectanglesToQuads(positions: Rectangle[], activeViewer: WebViewerInstance, pageNumber: number): any[] { const pageHeight = activeViewer.docViewer.getPageHeight(pageNumber); return positions.map((p) => this._rectangleToQuad(p, activeViewer, pageHeight)); @@ -132,20 +148,4 @@ export class AnnotationDrawService { return new activeViewer.CoreControls.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4); } - - annotationToQuads(annotation: Annotations.Annotation, activeViewer: WebViewerInstance) { - const x1 = annotation.getRect().x1; - const y1 = annotation.getRect().y1 + annotation.getRect().getHeight(); - - const x2 = annotation.getRect().x1 + annotation.getRect().getWidth(); - const y2 = annotation.getRect().y1 + annotation.getRect().getHeight(); - - const x3 = annotation.getRect().x1 + annotation.getRect().getWidth(); - const y3 = annotation.getRect().y1; - - const x4 = annotation.getRect().x1; - const y4 = annotation.getRect().y1; - - return new activeViewer.CoreControls.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4); - } } diff --git a/apps/red-ui/src/app/modules/projects/services/annotation-processing.service.ts b/apps/red-ui/src/app/modules/projects/services/annotation-processing.service.ts index 31583eef6..c2bd0f64d 100644 --- a/apps/red-ui/src/app/modules/projects/services/annotation-processing.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/annotation-processing.service.ts @@ -1,11 +1,25 @@ import { Injectable } from '@angular/core'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; -import { FilterModel } from '../../shared/components/filter/model/filter.model'; -import { handleCheckedValue } from '../../shared/components/filter/utils/filter-utils'; -import { SuperTypeSorter } from '../../../utils/sorters/super-type-sorter'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { FilterModel } from '@shared/components/filter/model/filter.model'; +import { handleCheckedValue } from '@shared/components/filter/utils/filter-utils'; +import { SuperTypeSorter } from '@utils/sorters/super-type-sorter'; @Injectable() export class AnnotationProcessingService { + static get secondaryAnnotationFilters(): FilterModel[] { + return [ + { + key: 'with-comments', + icon: 'red:comment', + label: 'filter-menu.with-comments', + checked: false, + topLevelFilter: true, + filters: [], + checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0 + } + ]; + } + getAnnotationFilter(annotations: AnnotationWrapper[]): FilterModel[] { const filterMap = new Map(); const filters: FilterModel[] = []; @@ -51,19 +65,6 @@ export class AnnotationProcessingService { return filters.sort((a, b) => SuperTypeSorter[a.key] - SuperTypeSorter[b.key]); } - private _createParentFilter(key: string, filterMap: Map, filters: FilterModel[]) { - const filter: FilterModel = { - key: key, - topLevelFilter: true, - matches: 1, - label: 'annotation-type.' + key, - filters: [] - }; - filterMap.set(key, filter); - filters.push(filter); - return filter; - } - filterAndGroupAnnotations( annotations: AnnotationWrapper[], primaryFilters: FilterModel[], @@ -106,6 +107,19 @@ export class AnnotationProcessingService { return obj; } + private _createParentFilter(key: string, filterMap: Map, filters: FilterModel[]) { + const filter: FilterModel = { + key: key, + topLevelFilter: true, + matches: 1, + label: 'annotation-type.' + key, + filters: [] + }; + filterMap.set(key, filter); + filters.push(filter); + return filter; + } + private _getFlatFilters(filters: FilterModel[], filterBy?: (f: FilterModel) => boolean) { const flatFilters: FilterModel[] = []; @@ -156,18 +170,4 @@ export class AnnotationProcessingService { return ann1.pageNumber < ann2.pageNumber ? -1 : 1; }); } - - static get secondaryAnnotationFilters(): FilterModel[] { - return [ - { - key: 'with-comments', - icon: 'red:comment', - label: 'filter-menu.with-comments', - checked: false, - topLevelFilter: true, - filters: [], - checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0 - } - ]; - } } diff --git a/apps/red-ui/src/app/modules/projects/services/file-action.service.ts b/apps/red-ui/src/app/modules/projects/services/file-action.service.ts index 8009b2d57..a0396676b 100644 --- a/apps/red-ui/src/app/modules/projects/services/file-action.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/file-action.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; -import { AppStateService } from '../../../state/app-state.service'; -import { UserService } from '../../../services/user.service'; +import { AppStateService } from '@state/app-state.service'; +import { UserService } from '@services/user.service'; import { FileStatus, ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http'; -import { FileStatusWrapper } from '../../../models/file/file-status.wrapper'; -import { PermissionsService } from '../../../services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { PermissionsService } from '@services/permissions.service'; import { isArray } from 'rxjs/internal-compatibility'; import { ProjectsDialogService } from './projects-dialog.service'; @@ -40,15 +40,6 @@ export class FileActionService { } } - private _openAssignReviewerDialog(file?: FileStatusWrapper, callback?: Function) { - this._dialogService.openAssignFileReviewerDialog(file ? file : this._appStateService.activeFile, async () => { - await this._appStateService.reloadActiveProjectFiles(); - if (callback) { - callback(); - } - }); - } - assignProjectReviewer(file?: FileStatus, callback?: Function, ignoreDialogChanges = false) { this._dialogService.openAssignFileReviewerDialog( file ? file : this._appStateService.activeFile, @@ -72,14 +63,6 @@ export class FileActionService { } } - private async _assignReviewerToCurrentUser(file?: FileStatus, callback?: Function) { - await this._statusControllerService.setFileReviewer(this._appStateService.activeProjectId, file.fileId, this._userService.userId).toPromise(); - await this._appStateService.reloadActiveProjectFiles(); - if (callback) { - await callback(); - } - } - setFileUnderApproval(fileStatus: FileStatusWrapper | FileStatusWrapper[]) { if (!isArray(fileStatus)) { fileStatus = [fileStatus]; @@ -119,4 +102,21 @@ export class FileActionService { this._appStateService.activeProjectId ); } + + private _openAssignReviewerDialog(file?: FileStatusWrapper, callback?: Function) { + this._dialogService.openAssignFileReviewerDialog(file ? file : this._appStateService.activeFile, async () => { + await this._appStateService.reloadActiveProjectFiles(); + if (callback) { + callback(); + } + }); + } + + private async _assignReviewerToCurrentUser(file?: FileStatus, callback?: Function) { + await this._statusControllerService.setFileReviewer(this._appStateService.activeProjectId, file.fileId, this._userService.userId).toPromise(); + await this._appStateService.reloadActiveProjectFiles(); + if (callback) { + await callback(); + } + } } diff --git a/apps/red-ui/src/app/modules/projects/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/projects/services/manual-annotation.service.ts index 59f9f921b..30a847f5a 100644 --- a/apps/red-ui/src/app/modules/projects/services/manual-annotation.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/manual-annotation.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; -import { AppStateService } from '../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { AddRedactionRequest, DictionaryControllerService, ForceRedactionRequest, ManualRedactionControllerService } from '@redaction/red-ui-http'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; -import { NotificationService, NotificationType } from '../../../services/notification.service'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { tap } from 'rxjs/operators'; -import { UserService } from '../../../services/user.service'; -import { PermissionsService } from '../../../services/permissions.service'; +import { UserService } from '@services/user.service'; +import { PermissionsService } from '@services/permissions.service'; @Injectable() export class ManualAnnotationService { @@ -182,6 +182,28 @@ export class ManualAnnotationService { } } + getTitle(type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE') { + if (this._permissionsService.isManagerAndOwner()) { + switch (type) { + case 'DICTIONARY': + return 'manual-annotation.dialog.header.dictionary'; + case 'FALSE_POSITIVE': + return 'manual-annotation.dialog.header.false-positive'; + case 'REDACTION': + return 'manual-annotation.dialog.header.redaction'; + } + } else { + switch (type) { + case 'DICTIONARY': + return 'manual-annotation.dialog.header.request-dictionary'; + case 'FALSE_POSITIVE': + return 'manual-annotation.dialog.header.request-false-positive'; + case 'REDACTION': + return 'manual-annotation.dialog.header.request-redaction'; + } + } + } + private _makeForceRedactionRequest(forceRedactionRequest: ForceRedactionRequest) { return this._manualRedactionControllerService .requestForceRedaction(forceRedactionRequest, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId) @@ -233,28 +255,6 @@ export class ManualAnnotationService { }); } - getTitle(type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE') { - if (this._permissionsService.isManagerAndOwner()) { - switch (type) { - case 'DICTIONARY': - return 'manual-annotation.dialog.header.dictionary'; - case 'FALSE_POSITIVE': - return 'manual-annotation.dialog.header.false-positive'; - case 'REDACTION': - return 'manual-annotation.dialog.header.redaction'; - } - } else { - switch (type) { - case 'DICTIONARY': - return 'manual-annotation.dialog.header.request-dictionary'; - case 'FALSE_POSITIVE': - return 'manual-annotation.dialog.header.request-false-positive'; - case 'REDACTION': - return 'manual-annotation.dialog.header.request-redaction'; - } - } - } - private _getMessage( mode: 'add' | 'remove' | 'request-remove' | 'suggest' | 'approve' | 'decline' | 'undo', modifyDictionary?: boolean, diff --git a/apps/red-ui/src/app/modules/projects/services/pdf-viewer-data.service.ts b/apps/red-ui/src/app/modules/projects/services/pdf-viewer-data.service.ts index 2e8485ce1..b064f9225 100644 --- a/apps/red-ui/src/app/modules/projects/services/pdf-viewer-data.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/pdf-viewer-data.service.ts @@ -7,10 +7,10 @@ import { RedactionLogControllerService, ViewedPagesControllerService } from '@redaction/red-ui-http'; -import { FileDataModel } from '../../../models/file/file-data.model'; -import { AppStateService } from '../../../state/app-state.service'; -import { PermissionsService } from '../../../services/permissions.service'; -import { FileStatusWrapper } from '../../../models/file/file-status.wrapper'; +import { FileDataModel } from '@models/file/file-data.model'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; @Injectable() export class PdfViewerDataService { diff --git a/apps/red-ui/src/app/modules/projects/services/projects-dialog.service.ts b/apps/red-ui/src/app/modules/projects/services/projects-dialog.service.ts index 7055d7d6b..1f977cc7f 100644 --- a/apps/red-ui/src/app/modules/projects/services/projects-dialog.service.ts +++ b/apps/red-ui/src/app/modules/projects/services/projects-dialog.service.ts @@ -9,14 +9,14 @@ import { } from '@redaction/red-ui-http'; import { AddEditProjectDialogComponent } from '../dialogs/add-edit-project-dialog/add-edit-project-dialog.component'; import { RemoveAnnotationsDialogComponent } from '../dialogs/remove-annotations-dialog/remove-annotations-dialog.component'; -import { NotificationService, NotificationType } from '../../../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { ForceRedactionDialogComponent } from '../dialogs/force-redaction-dialog/force-redaction-dialog.component'; -import { AnnotationWrapper } from '../../../models/file/annotation.wrapper'; -import { ConfirmationDialogComponent, ConfirmationDialogInput } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component'; -import { ProjectWrapper } from '../../../state/model/project.wrapper'; +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { ConfirmationDialogComponent, ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component'; +import { ProjectWrapper } from '@state/model/project.wrapper'; import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/document-info-dialog.component'; -import { AppStateService } from '../../../state/app-state.service'; -import { ManualRedactionEntryWrapper } from '../../../models/file/manual-redaction-entry.wrapper'; +import { AppStateService } from '@state/app-state.service'; +import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; import { ManualAnnotationService } from './manual-annotation.service'; import { TranslateService } from '@ngx-translate/core'; import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component'; diff --git a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts index 21ff8dba9..7a578b694 100644 --- a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts +++ b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts @@ -3,8 +3,8 @@ import { FilterModel } from '../components/filter/model/filter.model'; import { getFilteredEntities } from '../components/filter/utils/filter-utils'; import { FilterComponent } from '../components/filter/filter.component'; import { FormBuilder, FormGroup } from '@angular/forms'; -import { debounce } from '../../../utils/debounce'; -import { ScreenName, SortingOption, SortingService } from '../../../services/sorting.service'; +import { debounce } from '@utils/debounce'; +import { ScreenName, SortingOption, SortingService } from '@services/sorting.service'; // Functionalities: Filter, search, select, sort @@ -28,12 +28,50 @@ export abstract class BaseListingComponent { protected readonly _selectionKey: string; protected readonly _sortKey: ScreenName; + protected constructor(protected readonly _injector: Injector) { + this._formBuilder = this._injector.get(FormBuilder); + this._changeDetectorRef = this._injector.get(ChangeDetectorRef); + this._sortingService = this._injector.get(SortingService); + this._initSearch(); + } + + get hasActiveFilters() { + return ( + this._filterComponents.filter((f) => !!f).reduce((prev, component) => prev || component?.hasActiveFilters, false) || + this.searchForm.get('query').value + ); + } + + get areAllEntitiesSelected() { + return this.displayedEntities.length !== 0 && this.selectedEntitiesIds.length === this.displayedEntities.length; + } + + get areSomeEntitiesSelected() { + return this.selectedEntitiesIds.length > 0; + } + + get sortingOption(): SortingOption { + return this._sortingService.getSortingOption(this._getSortKey); + } + + protected get _filters(): { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[] { + return []; + } + + protected get _filterComponents(): FilterComponent[] { + return []; + } + + // ---- + private get _getSearchKey(): string { if (!this._searchKey) throw new Error('Not implemented'); return this._searchKey; } + // Search + private get _getSelectionKey(): string { if (!this._selectionKey) throw new Error('Not implemented'); @@ -46,41 +84,63 @@ export abstract class BaseListingComponent { return this._sortKey; } - protected get _filters(): { values: FilterModel[]; checker: Function; matchAll?: boolean; checkerArgs?: any }[] { - return []; + filtersChanged(filters?: { [key: string]: FilterModel[] }): void { + if (filters) { + for (const key of Object.keys(filters)) { + for (let idx = 0; idx < this[key].length; ++idx) { + this[key][idx] = filters[key][idx]; + } + } + } + this._filterEntities(); } + resetFilters() { + for (const filterComponent of this._filterComponents.filter((f) => !!f)) { + filterComponent.deactivateAllFilters(); + } + this.filtersChanged(); + this.searchForm.reset({ query: '' }); + } + + // Filter + + toggleEntitySelected($event: MouseEvent, entity: T) { + $event.stopPropagation(); + const idx = this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]); + if (idx === -1) { + this.selectedEntitiesIds.push(entity[this._getSelectionKey]); + } else { + this.selectedEntitiesIds.splice(idx, 1); + } + } + + toggleSelectAll() { + if (this.areSomeEntitiesSelected) { + this.selectedEntitiesIds = []; + } else { + this.selectedEntitiesIds = this.displayedEntities.map((entity) => entity[this._getSelectionKey]); + } + } + + isEntitySelected(entity: T) { + return this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]) !== -1; + } + + toggleSort($event) { + this._sortingService.toggleSort(this._getSortKey, $event); + } + + // Selection + protected _preFilter() { return; } - protected get _filterComponents(): FilterComponent[] { - return []; - } - protected _searchField(entity: T): string { return entity[this._getSearchKey]; } - // ---- - - protected constructor(protected readonly _injector: Injector) { - this._formBuilder = this._injector.get(FormBuilder); - this._changeDetectorRef = this._injector.get(ChangeDetectorRef); - this._sortingService = this._injector.get(SortingService); - this._initSearch(); - } - - // Search - - private _initSearch() { - this.searchForm = this._formBuilder.group({ - query: [''] - }); - - this.searchForm.valueChanges.subscribe(() => this._executeSearch()); - } - @debounce(200) protected _executeSearch() { this._executeSearchImmediately(); @@ -101,18 +161,7 @@ export abstract class BaseListingComponent { } } - // Filter - - filtersChanged(filters?: { [key: string]: FilterModel[] }): void { - if (filters) { - for (const key of Object.keys(filters)) { - for (let idx = 0; idx < this[key].length; ++idx) { - this[key][idx] = filters[key][idx]; - } - } - } - this._filterEntities(); - } + // Sort protected _filterEntities() { this._preFilter(); @@ -121,60 +170,11 @@ export abstract class BaseListingComponent { this._changeDetectorRef.detectChanges(); } - resetFilters() { - for (const filterComponent of this._filterComponents.filter((f) => !!f)) { - filterComponent.deactivateAllFilters(); - } - this.filtersChanged(); - this.searchForm.reset({ query: '' }); - } + private _initSearch() { + this.searchForm = this._formBuilder.group({ + query: [''] + }); - get hasActiveFilters() { - return ( - this._filterComponents.filter((f) => !!f).reduce((prev, component) => prev || component?.hasActiveFilters, false) || - this.searchForm.get('query').value - ); - } - - // Selection - - toggleEntitySelected($event: MouseEvent, entity: T) { - $event.stopPropagation(); - const idx = this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]); - if (idx === -1) { - this.selectedEntitiesIds.push(entity[this._getSelectionKey]); - } else { - this.selectedEntitiesIds.splice(idx, 1); - } - } - - toggleSelectAll() { - if (this.areSomeEntitiesSelected) { - this.selectedEntitiesIds = []; - } else { - this.selectedEntitiesIds = this.displayedEntities.map((entity) => entity[this._getSelectionKey]); - } - } - - get areAllEntitiesSelected() { - return this.displayedEntities.length !== 0 && this.selectedEntitiesIds.length === this.displayedEntities.length; - } - - get areSomeEntitiesSelected() { - return this.selectedEntitiesIds.length > 0; - } - - isEntitySelected(entity: T) { - return this.selectedEntitiesIds.indexOf(entity[this._getSelectionKey]) !== -1; - } - - // Sort - - get sortingOption(): SortingOption { - return this._sortingService.getSortingOption(this._getSortKey); - } - - toggleSort($event) { - this._sortingService.toggleSort(this._getSortKey, $event); + this.searchForm.valueChanges.subscribe(() => this._executeSearch()); } } diff --git a/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts b/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts index 29d7534ea..8713aeb5a 100644 --- a/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/annotation-icon/annotation-icon.component.ts @@ -16,10 +16,6 @@ export class AnnotationIconComponent implements OnInit { constructor() {} - ngOnInit() { - this.icon.nativeElement.style.setProperty('--color', this.backgroundColor); - } - get isHint() { return this.type === 'circle' || this.dictType?.type === 'hint'; } @@ -35,4 +31,8 @@ export class AnnotationIconComponent implements OnInit { get backgroundColor() { return this.color || this.dictType?.hexColor; } + + ngOnInit() { + this.icon.nativeElement.style.setProperty('--color', this.backgroundColor); + } } 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 a185664fe..1e1e3a6a1 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 @@ -1,9 +1,9 @@ import { Component, Input } from '@angular/core'; -import { PermissionsService } from '../../../../../services/permissions.service'; -import { ProjectWrapper } from '../../../../../state/model/project.wrapper'; -import { FileStatusWrapper } from '../../../../../models/file/file-status.wrapper'; -import { FileDownloadService } from '../../../../upload-download/services/file-download.service'; -import { NotificationService } from '../../../../../services/notification.service'; +import { PermissionsService } from '@services/permissions.service'; +import { ProjectWrapper } from '@state/model/project.wrapper'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { FileDownloadService } from '@upload-download/services/file-download.service'; +import { NotificationService } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; export type MenuState = 'OPEN' | 'CLOSED'; diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/icon-button/icon-button.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/icon-button/icon-button.component.ts index e9684fa0b..8628c69b3 100644 --- a/apps/red-ui/src/app/modules/shared/components/buttons/icon-button/icon-button.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/buttons/icon-button/icon-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ selector: 'redaction-icon-button', diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/user-button/user-button.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/user-button/user-button.component.ts index 9d8f23387..1e7ecb76a 100644 --- a/apps/red-ui/src/app/modules/shared/components/buttons/user-button/user-button.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/buttons/user-button/user-button.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core'; -import { UserWrapper } from '../../../../../services/user.service'; +import { UserWrapper } from '@services/user.service'; @Component({ selector: 'redaction-user-button', diff --git a/apps/red-ui/src/app/modules/shared/components/dictionary-annotation-icon/dictionary-annotation-icon.component.ts b/apps/red-ui/src/app/modules/shared/components/dictionary-annotation-icon/dictionary-annotation-icon.component.ts index 696268e5b..95806e5bf 100644 --- a/apps/red-ui/src/app/modules/shared/components/dictionary-annotation-icon/dictionary-annotation-icon.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/dictionary-annotation-icon/dictionary-annotation-icon.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { AppStateService } from '../../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { TypeValue } from '@redaction/red-ui-http'; @Component({ diff --git a/apps/red-ui/src/app/modules/shared/components/filter/filter.component.ts b/apps/red-ui/src/app/modules/shared/components/filter/filter.component.ts index b26c479b5..41ef4094a 100644 --- a/apps/red-ui/src/app/modules/shared/components/filter/filter.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/filter/filter.component.ts @@ -35,6 +35,19 @@ export class FilterComponent implements OnChanges { constructor(private readonly _changeDetectorRef: ChangeDetectorRef) {} + get hasActiveFilters(): boolean { + for (const filter of this._allFilters) { + if (filter.checked || filter.indeterminate) { + return true; + } + } + return false; + } + + private get _allFilters(): FilterModel[] { + return [...(this.primaryFilters ?? []), ...(this.secondaryFilters ?? [])]; + } + ngOnChanges(): void { this.atLeastOneFilterIsExpandable = false; this.atLeastOneSecondaryFilterIsExpandable = false; @@ -71,15 +84,6 @@ export class FilterComponent implements OnChanges { this._setAllFilters(false); } - get hasActiveFilters(): boolean { - for (const filter of this._allFilters) { - if (filter.checked || filter.indeterminate) { - return true; - } - } - return false; - } - applyFilters() { this.filtersChanged.emit({ primary: this.primaryFilters, secondary: this.secondaryFilters }); } @@ -89,21 +93,6 @@ export class FilterComponent implements OnChanges { filter.expanded = !filter.expanded; } - private _setAllFilters(value: boolean) { - const filters = value ? this.primaryFilters : this._allFilters; - filters.forEach((f) => { - f.checked = value; - f.indeterminate = false; - f.filters?.forEach((ff) => { - ff.checked = value; - }); - }); - } - - private get _allFilters(): FilterModel[] { - return [...(this.primaryFilters ?? []), ...(this.secondaryFilters ?? [])]; - } - filterMouseEnter() { this.mouseOver = true; if (this.mouseOverTimeout) { @@ -125,4 +114,15 @@ export class FilterComponent implements OnChanges { _(obj): FilterModel { return obj as FilterModel; } + + private _setAllFilters(value: boolean) { + const filters = value ? this.primaryFilters : this._allFilters; + filters.forEach((f) => { + f.checked = value; + f.indeterminate = false; + f.filters?.forEach((ff) => { + ff.checked = value; + }); + }); + } } diff --git a/apps/red-ui/src/app/modules/shared/components/filter/utils/filter-utils.ts b/apps/red-ui/src/app/modules/shared/components/filter/utils/filter-utils.ts index c3f5a8251..b80a9a7ad 100644 --- a/apps/red-ui/src/app/modules/shared/components/filter/utils/filter-utils.ts +++ b/apps/red-ui/src/app/modules/shared/components/filter/utils/filter-utils.ts @@ -1,7 +1,7 @@ import { FilterModel } from '../model/filter.model'; -import { FileStatusWrapper } from '../../../../../models/file/file-status.wrapper'; -import { ProjectWrapper } from '../../../../../state/model/project.wrapper'; -import { PermissionsService } from '../../../../../services/permissions.service'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; +import { PermissionsService } from '@services/permissions.service'; export function processFilters(oldFilters: FilterModel[], newFilters: FilterModel[]) { copySettings(oldFilters, newFilters); diff --git a/apps/red-ui/src/app/modules/shared/components/hidden-action/hidden-action.component.ts b/apps/red-ui/src/app/modules/shared/components/hidden-action/hidden-action.component.ts index 82aba4c8c..c524ee4b1 100644 --- a/apps/red-ui/src/app/modules/shared/components/hidden-action/hidden-action.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/hidden-action/hidden-action.component.ts @@ -6,11 +6,10 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; styleUrls: ['./hidden-action.component.scss'] }) export class HiddenActionComponent { - private _clickCount = 0; - private _clickCountTimeout: any; - @Input() requiredClicks = 4; @Output() action = new EventEmitter(); + private _clickCount = 0; + private _clickCountTimeout: any; countActions() { this._clickCount += 1; diff --git a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts index ceedfc18c..5bd34c31c 100644 --- a/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/initials-avatar/initials-avatar.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnChanges } from '@angular/core'; -import { UserService } from '../../../../services/user.service'; +import { UserService } from '@services/user.service'; import { User } from '@redaction/red-ui-http'; import { TranslateService } from '@ngx-translate/core'; @@ -25,6 +25,27 @@ export class InitialsAvatarComponent implements OnChanges { constructor(private readonly _userService: UserService, private readonly _translateService: TranslateService) {} + get hasBorder(): boolean { + return !!this.user && this._userService.userId !== this.userId && this._userService.isManager(this.user); + } + + get disabled(): boolean { + return !this._userService.isActive(this.user); + } + + private get _colorClass() { + if (this._userService.userId === this.userId) { + return 'red-white'; + } + if (this.disabled) { + return 'inactive'; + } + if (this.color.includes('-')) { + return this.color; + } + return `${this.color}-dark`; + } + ngOnChanges(): void { const isSystemUser = this.userId && this.userId.toLowerCase() === 'system'; if (isSystemUser) { diff --git a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts index d72bf41e7..951c66126 100644 --- a/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/pagination/pagination.component.ts @@ -8,10 +8,21 @@ const DISPLAYED_ITEMS = 5; styleUrls: ['./pagination.component.scss'] }) export class PaginationComponent { + displayedPages: (number | string)[]; + @Output() pageChanged = new EventEmitter(); + displayed; + private _currentPage: number; + + get currentPage() { + return this._currentPage; + } + private _totalPages: number; - displayedPages: (number | string)[]; + get totalPages() { + return this._totalPages; + } @Input() set settings(value: { currentPage: number; totalPages: number }) { @@ -20,17 +31,19 @@ export class PaginationComponent { this._updatePagesArray(); } - get currentPage() { - return this._currentPage; + get allDisplayed(): boolean { + return this.totalPages > DISPLAYED_ITEMS; } - get totalPages() { - return this._totalPages; + selectPage(page: number | string) { + if (page !== '...') { + this.pageChanged.emit(page as number); + } } - @Output() pageChanged = new EventEmitter(); - - displayed; + displayValue(page: number | string) { + return page === '...' ? page : (page as number) + 1; + } private _updatePagesArray() { this.displayedPages = [0]; @@ -47,18 +60,4 @@ export class PaginationComponent { this.displayedPages.push(this.totalPages - 1); } } - - get allDisplayed(): boolean { - return this.totalPages > DISPLAYED_ITEMS; - } - - selectPage(page: number | string) { - if (page !== '...') { - this.pageChanged.emit(page as number); - } - } - - displayValue(page: number | string) { - return page === '...' ? page : (page as number) + 1; - } } diff --git a/apps/red-ui/src/app/modules/shared/components/search-input/search-input.component.ts b/apps/red-ui/src/app/modules/shared/components/search-input/search-input.component.ts index 8b121e247..b7de2c42d 100644 --- a/apps/red-ui/src/app/modules/shared/components/search-input/search-input.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/search-input/search-input.component.ts @@ -15,11 +15,11 @@ export class SearchInputComponent { return !!this.form.get('query').value.length; } - clearContent() { - this.form.patchValue({ query: '' }); - } - get computedWidth() { return this.width === 'full' ? '100%' : `${this.width}px`; } + + clearContent() { + this.form.patchValue({ query: '' }); + } } diff --git a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts index 383f024fa..c49df9f0a 100644 --- a/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; -import { Color } from '../../../../utils/types'; +import { Color } from '@utils/types'; import { FilterModel } from '../filter/model/filter.model'; export class DoughnutChartConfig { @@ -36,17 +36,6 @@ export class SimpleDoughnutChartComponent implements OnChanges { constructor() {} - ngOnChanges(): void { - this.calculateChartData(); - this.cx = this.radius + this.strokeWidth / 2; - this.cy = this.radius + this.strokeWidth / 2; - this.size = this.strokeWidth + this.radius * 2; - this.parsedConfig = this.config.map((el) => ({ - ...el, - checked: this.filter?.find((f) => f.key === el.key)?.checked - })); - } - get circumference() { return 2 * Math.PI * this.radius; } @@ -59,6 +48,17 @@ export class SimpleDoughnutChartComponent implements OnChanges { return this.totalType === 'sum' ? this.dataTotal : this.config.length; } + ngOnChanges(): void { + this.calculateChartData(); + this.cx = this.radius + this.strokeWidth / 2; + this.cy = this.radius + this.strokeWidth / 2; + this.size = this.strokeWidth + this.radius * 2; + this.parsedConfig = this.config.map((el) => ({ + ...el, + checked: this.filter?.find((f) => f.key === el.key)?.checked + })); + } + calculateChartData() { const newData = []; let angleOffset = -90; diff --git a/apps/red-ui/src/app/modules/shared/components/status-bar/status-bar.component.ts b/apps/red-ui/src/app/modules/shared/components/status-bar/status-bar.component.ts index 1e4a964d5..1b7cfcbb5 100644 --- a/apps/red-ui/src/app/modules/shared/components/status-bar/status-bar.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/status-bar/status-bar.component.ts @@ -1,5 +1,5 @@ import { Component, Input, ViewEncapsulation } from '@angular/core'; -import { Color } from '../../../../utils/types'; +import { Color } from '@utils/types'; @Component({ selector: 'redaction-status-bar', diff --git a/apps/red-ui/src/app/modules/shared/components/table-col-name/table-col-name.component.ts b/apps/red-ui/src/app/modules/shared/components/table-col-name/table-col-name.component.ts index b3cda5121..34328716e 100644 --- a/apps/red-ui/src/app/modules/shared/components/table-col-name/table-col-name.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/table-col-name/table-col-name.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; -import { SortingOption } from '../../../../services/sorting.service'; +import { SortingOption } from '@services/sorting.service'; @Component({ selector: 'redaction-table-col-name', diff --git a/apps/red-ui/src/app/modules/shared/directives/has-scrollbar.directive.ts b/apps/red-ui/src/app/modules/shared/directives/has-scrollbar.directive.ts index c9d8cb664..d0c832652 100644 --- a/apps/red-ui/src/app/modules/shared/directives/has-scrollbar.directive.ts +++ b/apps/red-ui/src/app/modules/shared/directives/has-scrollbar.directive.ts @@ -5,9 +5,13 @@ import { AfterContentChecked, Directive, ElementRef, HostBinding } from '@angula exportAs: 'redactionHasScrollbar' }) export class HasScrollbarDirective implements AfterContentChecked { + @HostBinding('class') class = ''; + constructor(private readonly _elementRef: ElementRef) {} - @HostBinding('class') class = ''; + get hasScrollbar() { + return this._elementRef?.nativeElement.clientHeight < this._elementRef?.nativeElement.scrollHeight; + } ngAfterContentChecked() { this._process(); @@ -19,8 +23,4 @@ export class HasScrollbarDirective implements AfterContentChecked { this.class = newClass; } } - - get hasScrollbar() { - return this._elementRef?.nativeElement.clientHeight < this._elementRef?.nativeElement.scrollHeight; - } } diff --git a/apps/red-ui/src/app/modules/shared/directives/navigate-last-projects-screen.directive.ts b/apps/red-ui/src/app/modules/shared/directives/navigate-last-projects-screen.directive.ts index a950afc44..d99f0d5a7 100644 --- a/apps/red-ui/src/app/modules/shared/directives/navigate-last-projects-screen.directive.ts +++ b/apps/red-ui/src/app/modules/shared/directives/navigate-last-projects-screen.directive.ts @@ -1,5 +1,5 @@ import { Directive, HostListener } from '@angular/core'; -import { RouterHistoryService } from '../../../services/router-history.service'; +import { RouterHistoryService } from '@services/router-history.service'; @Directive({ selector: '[redactionNavigateLastProjectsScreen]' diff --git a/apps/red-ui/src/app/modules/shared/directives/sync-width.directive.ts b/apps/red-ui/src/app/modules/shared/directives/sync-width.directive.ts index 3cf86d846..495cbeb5e 100644 --- a/apps/red-ui/src/app/modules/shared/directives/sync-width.directive.ts +++ b/apps/red-ui/src/app/modules/shared/directives/sync-width.directive.ts @@ -1,5 +1,5 @@ import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnDestroy } from '@angular/core'; -import { debounce } from '../../../utils/debounce'; +import { debounce } from '@utils/debounce'; @Directive({ selector: '[redactionSyncWidth]', diff --git a/apps/red-ui/src/app/modules/shared/pipes/humanize.pipe.ts b/apps/red-ui/src/app/modules/shared/pipes/humanize.pipe.ts index b791a2446..1d9b329bf 100644 --- a/apps/red-ui/src/app/modules/shared/pipes/humanize.pipe.ts +++ b/apps/red-ui/src/app/modules/shared/pipes/humanize.pipe.ts @@ -1,5 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; -import { humanize } from '../../../utils/functions'; +import { humanize } from '@utils/functions'; @Pipe({ name: 'humanize' diff --git a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts index 4749518b8..2c772f8c7 100644 --- a/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts +++ b/apps/red-ui/src/app/modules/upload-download/file-drop/file-drop.component.ts @@ -3,8 +3,8 @@ import { FileUploadService } from '../services/file-upload.service'; import { FileUploadModel } from '../model/file-upload.model'; import { OverlayRef } from '@angular/cdk/overlay'; import { StatusOverlayService } from '../services/status-overlay.service'; -import { handleFileDrop } from '../../../utils/file-drop-utils'; -import { AppStateService } from '../../../state/app-state.service'; +import { handleFileDrop } from '@utils/file-drop-utils'; +import { AppStateService } from '@state/app-state.service'; @Component({ selector: 'redaction-file-drop', diff --git a/apps/red-ui/src/app/modules/upload-download/file-upload-download.module.ts b/apps/red-ui/src/app/modules/upload-download/file-upload-download.module.ts index 9d7ccce07..e045fab1e 100644 --- a/apps/red-ui/src/app/modules/upload-download/file-upload-download.module.ts +++ b/apps/red-ui/src/app/modules/upload-download/file-upload-download.module.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { FileDropComponent } from './file-drop/file-drop.component'; import { OverlayModule } from '@angular/cdk/overlay'; import { UploadStatusOverlayComponent } from './upload-status-overlay/upload-status-overlay.component'; -import { SharedModule } from '../shared/shared.module'; +import { SharedModule } from '@shared/shared.module'; import { UploadDownloadDialogService } from './services/upload-download-dialog.service'; import { OverwriteFilesDialogComponent } from './dialogs/overwrite-files-dialog/overwrite-files-dialog.component'; import { FileUploadService } from './services/file-upload.service'; 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 722b618c9..82fdb953c 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 @@ -1,14 +1,14 @@ import { ApplicationRef, Injectable } from '@angular/core'; import { DownloadControllerService, FileManagementControllerService } from '@redaction/red-ui-http'; import { interval, Observable } from 'rxjs'; -import { AppConfigKey, AppConfigService } from '../../app-config/app-config.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { TranslateService } from '@ngx-translate/core'; -import { ProjectWrapper } from '../../../state/model/project.wrapper'; -import { FileStatusWrapper } from '../../../models/file/file-status.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { mergeMap, tap } from 'rxjs/operators'; import { DownloadStatusWrapper } from '../model/download-status.wrapper'; -import { AppStateService } from '../../../state/app-state.service'; -import { PermissionsService } from '../../../services/permissions.service'; +import { AppStateService } from '@state/app-state.service'; +import { PermissionsService } from '@services/permissions.service'; import { KeycloakService } from 'keycloak-angular'; @Injectable() diff --git a/apps/red-ui/src/app/modules/upload-download/services/file-drop-overlay.service.ts b/apps/red-ui/src/app/modules/upload-download/services/file-drop-overlay.service.ts index 4ff2115af..8783d5b24 100644 --- a/apps/red-ui/src/app/modules/upload-download/services/file-drop-overlay.service.ts +++ b/apps/red-ui/src/app/modules/upload-download/services/file-drop-overlay.service.ts @@ -53,13 +53,6 @@ export class FileDropOverlayService { document.getElementsByTagName('body')[0].removeEventListener('mouseout', this.mouseOut, false); } - private _createInjector() { - return Injector.create({ - providers: [{ provide: OverlayRef, useValue: this._dropOverlayRef }], - parent: this._injector - }); - } - openFileDropOverlay() { const component = new ComponentPortal(FileDropComponent, null, this._createInjector()); if (!this._dropOverlayRef.hasAttached()) { @@ -72,4 +65,11 @@ export class FileDropOverlayService { this._dropOverlayRef.detach(); } } + + private _createInjector() { + return Injector.create({ + providers: [{ provide: OverlayRef, useValue: this._dropOverlayRef }], + parent: this._injector + }); + } } diff --git a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts index 23053921a..1329b6f0a 100644 --- a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts +++ b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts @@ -1,13 +1,13 @@ import { ApplicationRef, Injectable } from '@angular/core'; import { FileUploadModel } from '../model/file-upload.model'; -import { AppStateService } from '../../../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { HttpEventType } from '@angular/common/http'; import { FileManagementControllerService } from '@redaction/red-ui-http'; import { interval, Subscription } from 'rxjs'; -import { AppConfigKey, AppConfigService } from '../../app-config/app-config.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { TranslateService } from '@ngx-translate/core'; import { UploadDownloadDialogService } from './upload-download-dialog.service'; -import { toNumber } from '../../../utils/functions'; +import { toNumber } from '@utils/functions'; export interface ActiveUpload { subscription: Subscription; @@ -37,6 +37,10 @@ export class FileUploadService { }); } + get activeProjectKeys() { + return Object.keys(this.groupedFiles).filter((projectId) => this.groupedFiles[projectId].length > 0); + } + scheduleUpload(item: FileUploadModel) { if (!item.sizeError) { item.progress = 0; @@ -84,13 +88,6 @@ export class FileUploadService { return files.length; } - private _addFileToGroup(file: FileUploadModel) { - if (!this.groupedFiles[file.projectId]) { - this.groupedFiles[file.projectId] = []; - } - this.groupedFiles[file.projectId].push(file); - } - filterFiles() { for (const file of this.files) { if (file.completed && !file.error) { @@ -99,20 +96,33 @@ export class FileUploadService { } } - private _removeFileFromGroup(file: FileUploadModel) { - const index = this.groupedFiles[file.projectId].indexOf(file); - this.groupedFiles[file.projectId].splice(index, 1); - } - - get activeProjectKeys() { - return Object.keys(this.groupedFiles).filter((projectId) => this.groupedFiles[projectId].length > 0); - } - stopAllUploads() { this.files = []; this.groupedFiles = {}; } + removeFile(item: FileUploadModel) { + this._removeUpload(item); + + const index = this.files.indexOf(item); + if (index > -1) { + this._removeFileFromGroup(item); + this.files.splice(index, 1); + } + } + + private _addFileToGroup(file: FileUploadModel) { + if (!this.groupedFiles[file.projectId]) { + this.groupedFiles[file.projectId] = []; + } + this.groupedFiles[file.projectId].push(file); + } + + private _removeFileFromGroup(file: FileUploadModel) { + const index = this.groupedFiles[file.projectId].indexOf(file); + this.groupedFiles[file.projectId].splice(index, 1); + } + private _handleUploads() { if (this._activeUploads.length < FileUploadService.MAX_PARALLEL_UPLOADS && this._pendingUploads.length > 0) { let cnt = FileUploadService.MAX_PARALLEL_UPLOADS - this._activeUploads.length; @@ -163,16 +173,6 @@ export class FileUploadService { return subscription; } - removeFile(item: FileUploadModel) { - this._removeUpload(item); - - const index = this.files.indexOf(item); - if (index > -1) { - this._removeFileFromGroup(item); - this.files.splice(index, 1); - } - } - private _removeUpload(fileUploadModel: FileUploadModel) { const index = this._activeUploads.findIndex((au) => au.fileUploadModel === fileUploadModel); if (index > -1) { diff --git a/apps/red-ui/src/app/modules/upload-download/services/status-overlay.service.ts b/apps/red-ui/src/app/modules/upload-download/services/status-overlay.service.ts index fe47689ff..2bd7148bd 100644 --- a/apps/red-ui/src/app/modules/upload-download/services/status-overlay.service.ts +++ b/apps/red-ui/src/app/modules/upload-download/services/status-overlay.service.ts @@ -11,17 +11,17 @@ export class StatusOverlayService { this._uploadStatusOverlayRef = this._overlay.create(); } - private _createUploadInjector() { - return Injector.create({ - providers: [{ provide: OverlayRef, useValue: this._uploadStatusOverlayRef }], - parent: this._injector - }); - } - openUploadStatusOverlay() { const component = new ComponentPortal(UploadStatusOverlayComponent, null, this._createUploadInjector()); if (!this._uploadStatusOverlayRef.hasAttached()) { this._uploadStatusOverlayRef.attach(component); } } + + private _createUploadInjector() { + return Injector.create({ + providers: [{ provide: OverlayRef, useValue: this._uploadStatusOverlayRef }], + parent: this._injector + }); + } } diff --git a/apps/red-ui/src/app/services/permissions.service.ts b/apps/red-ui/src/app/services/permissions.service.ts index 441aff79e..432ad4c6c 100644 --- a/apps/red-ui/src/app/services/permissions.service.ts +++ b/apps/red-ui/src/app/services/permissions.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@angular/core'; -import { AppStateService } from '../state/app-state.service'; +import { AppStateService } from '@state/app-state.service'; import { UserService, UserWrapper } from './user.service'; -import { FileStatusWrapper } from '../models/file/file-status.wrapper'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { User } from '@redaction/red-ui-http'; -import { ProjectWrapper } from '../state/model/project.wrapper'; +import { ProjectWrapper } from '@state/model/project.wrapper'; @Injectable({ providedIn: 'root' diff --git a/apps/red-ui/src/app/services/translate-chart.service.ts b/apps/red-ui/src/app/services/translate-chart.service.ts index b4a7e0235..94172a2b1 100644 --- a/apps/red-ui/src/app/services/translate-chart.service.ts +++ b/apps/red-ui/src/app/services/translate-chart.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { DoughnutChartConfig } from '../modules/shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; +import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component'; import { TranslateService } from '@ngx-translate/core'; @Injectable({ @@ -13,6 +13,9 @@ export class TranslateChartService { } translateRoles(config: DoughnutChartConfig[]): DoughnutChartConfig[] { - return config.map((val) => ({ ...val, label: this._translateService.instant(`roles.${val.label}`).toLowerCase() })); + return config.map((val) => ({ + ...val, + label: this._translateService.instant(`roles.${val.label}`).toLowerCase() + })); } } diff --git a/apps/red-ui/src/app/services/user.service.ts b/apps/red-ui/src/app/services/user.service.ts index de98f24ad..7454b9f8c 100644 --- a/apps/red-ui/src/app/services/user.service.ts +++ b/apps/red-ui/src/app/services/user.service.ts @@ -53,7 +53,6 @@ export class UserWrapper { }) export class UserService { private _currentUser: UserWrapper; - private _allUsers: User[]; constructor( @Inject(BASE_HREF) readonly baseHref: string, @@ -61,19 +60,16 @@ export class UserService { private readonly _userControllerService: UserControllerService ) {} - logout() { - wipeCaches(); - this._keycloakService.logout(window.location.origin + this.baseHref); + private _allUsers: User[]; + + get allUsers(): User[] { + return this._allUsers; } get userId(): string { return this._currentUser.id; } - get allUsers(): User[] { - return this._allUsers; - } - get managerUsers(): User[] { return this._allUsers.filter((u) => u.roles.indexOf('RED_MANAGER') >= 0); } @@ -82,6 +78,15 @@ export class UserService { return this._allUsers.filter((u) => u.roles.indexOf('RED_USER') >= 0 || u.roles.indexOf('RED_MANAGER') >= 0); } + get user() { + return this._currentUser; + } + + logout() { + wipeCaches(); + this._keycloakService.logout(window.location.origin + this.baseHref); + } + async loadAllUsersIfNecessary() { if (!this._allUsers) { await this.loadAllUsers(); @@ -101,10 +106,6 @@ export class UserService { this._currentUser = new UserWrapper(await this._keycloakService.loadUserProfile(false), this._keycloakService.getUserRoles(true), userId); } - get user() { - return this._currentUser; - } - getUserById(id: string) { return this._allUsers.find((u) => u.userId === id); } @@ -152,15 +153,6 @@ export class UserService { return user.roles?.length > 0; } - private _hasAnyRedRole(u: User) { - return ( - u.roles.indexOf('RED_USER') >= 0 || - u.roles.indexOf('RED_MANAGER') >= 0 || - u.roles.indexOf('RED_ADMIN') >= 0 || - u.roles.indexOf('RED_USER_ADMIN') >= 0 - ); - } - hasAnyRole(requiredRoles: string[], user?: User) { if (!user) { user = this.user; @@ -176,4 +168,13 @@ export class UserService { return true; } } + + private _hasAnyRedRole(u: User) { + return ( + u.roles.indexOf('RED_USER') >= 0 || + u.roles.indexOf('RED_MANAGER') >= 0 || + u.roles.indexOf('RED_ADMIN') >= 0 || + u.roles.indexOf('RED_USER_ADMIN') >= 0 + ); + } } diff --git a/apps/red-ui/src/app/state/app-state.guard.ts b/apps/red-ui/src/app/state/app-state.guard.ts index 60df77836..d6853bc96 100644 --- a/apps/red-ui/src/app/state/app-state.guard.ts +++ b/apps/red-ui/src/app/state/app-state.guard.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { AppStateService } from './app-state.service'; -import { UserService } from '../services/user.service'; +import { UserService } from '@services/user.service'; @Injectable({ providedIn: 'root' 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 c1d4ab192..651296d80 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -13,14 +13,14 @@ import { TypeValue, VersionsControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '../services/notification.service'; +import { NotificationService, NotificationType } from '@services/notification.service'; import { TranslateService } from '@ngx-translate/core'; import { Event, NavigationEnd, ResolveStart, Router } from '@angular/router'; -import { UserService } from '../services/user.service'; +import { UserService } from '@services/user.service'; import { forkJoin, Observable, of } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; -import { humanize } from '../utils/functions'; -import { FileStatusWrapper } from '../models/file/file-status.wrapper'; +import { humanize } from '@utils/functions'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { ProjectWrapper } from './model/project.wrapper'; export interface AppState { @@ -41,10 +41,9 @@ export interface AppState { providedIn: 'root' }) export class AppStateService { - private _appState: AppState; - private _dictionaryData: { [key: string]: { [key: string]: TypeValue } } = null; fileChanged = new EventEmitter(); fileReanalysed = new EventEmitter(); + private _appState: AppState; constructor( private readonly _router: Router, @@ -86,6 +85,82 @@ export class AppStateService { }); } + private _dictionaryData: { [key: string]: { [key: string]: TypeValue } } = null; + + get dictionaryData(): { [key: string]: { [key: string]: TypeValue } } { + return this._dictionaryData; + } + + get aggregatedFiles(): FileStatusWrapper[] { + const result: FileStatusWrapper[] = []; + this._appState.projects.forEach((p) => { + result.push(...p.files); + }); + return result; + } + + get activeRuleSetId(): string { + return this._appState.activeRuleSetId; + } + + get activeRuleSet(): RuleSetModel { + return this.getRuleSetById(this.activeRuleSetId); + } + + get ruleSets(): RuleSetModel[] { + return this._appState.ruleSets; + } + + get activeFileAttributesConfig(): FileAttributesConfig { + if (this.activeProject) { + return this._appState.fileAttributesConfig[this.activeProject.ruleSetId]; + } + } + + get activeDictionaryType(): string { + return this._appState.activeDictionaryType; + } + + get activeDictionary(): TypeValue { + return this.activeRuleSetId && this.dictionaryData[this.activeRuleSetId] ? this.dictionaryData[this.activeRuleSetId][this.activeDictionaryType] : null; + } + + get activeProjectId(): string { + return this._appState.activeProjectId; + } + + get activeProject(): ProjectWrapper { + return this._appState.projects.find((p) => p.projectId === this.activeProjectId); + } + + get allProjects(): ProjectWrapper[] { + return this._appState.projects; + } + + get hasProjects() { + return this.allProjects?.length > 0; + } + + get activeFile(): FileStatusWrapper { + return this.activeProject?.files.find((f) => f.fileId === this.activeFileId); + } + + get activeFileId(): string { + return this._appState.activeFileId; + } + + get totalAnalysedPages() { + return this._appState.totalAnalysedPages; + } + + get totalPeople() { + return this._appState.totalPeople; + } + + get totalDocuments() { + return this._appState.totalDocuments; + } + private static _isFileOverviewRoute(event: Event) { return event instanceof ResolveStart && event.url.includes('/main/projects/') && event.url.includes('/file/'); } @@ -126,10 +201,6 @@ export class AppStateService { return this._appState.versions[ruleSetId].rulesVersion; } - get dictionaryData(): { [key: string]: { [key: string]: TypeValue } } { - return this._dictionaryData; - } - getDictionaryColor(type?: string, ruleSetId?: string) { if (!ruleSetId && this.activeProject) { ruleSetId = this.activeProject.ruleSetId; @@ -157,44 +228,10 @@ export class AppStateService { return this._dictionaryData[ruleSetId][type]?.label; } - get aggregatedFiles(): FileStatusWrapper[] { - const result: FileStatusWrapper[] = []; - this._appState.projects.forEach((p) => { - result.push(...p.files); - }); - return result; - } - - get activeRuleSetId(): string { - return this._appState.activeRuleSetId; - } - - get activeRuleSet(): RuleSetModel { - return this.getRuleSetById(this.activeRuleSetId); - } - - get ruleSets(): RuleSetModel[] { - return this._appState.ruleSets; - } - getRuleSetById(id: string): RuleSetModel { return this.ruleSets.find((rs) => rs.ruleSetId === id); } - get activeFileAttributesConfig(): FileAttributesConfig { - if (this.activeProject) { - return this._appState.fileAttributesConfig[this.activeProject.ruleSetId]; - } - } - - get activeDictionaryType(): string { - return this._appState.activeDictionaryType; - } - - get activeDictionary(): TypeValue { - return this.activeRuleSetId && this.dictionaryData[this.activeRuleSetId] ? this.dictionaryData[this.activeRuleSetId][this.activeDictionaryType] : null; - } - getDictionaryTypeValue(key: string, ruleSetId?: string) { if (!ruleSetId && this.activeProject) { ruleSetId = this.activeProject.ruleSetId; @@ -211,42 +248,6 @@ export class AppStateService { return data ? data : this._dictionaryData[ruleSetId]['default']; } - get activeProjectId(): string { - return this._appState.activeProjectId; - } - - get activeProject(): ProjectWrapper { - return this._appState.projects.find((p) => p.projectId === this.activeProjectId); - } - - get allProjects(): ProjectWrapper[] { - return this._appState.projects; - } - - get hasProjects() { - return this.allProjects?.length > 0; - } - - get activeFile(): FileStatusWrapper { - return this.activeProject?.files.find((f) => f.fileId === this.activeFileId); - } - - get activeFileId(): string { - return this._appState.activeFileId; - } - - get totalAnalysedPages() { - return this._appState.totalAnalysedPages; - } - - get totalPeople() { - return this._appState.totalPeople; - } - - get totalDocuments() { - return this._appState.totalDocuments; - } - getProjectById(id: string) { return this.allProjects.find((project) => project.project.projectId === id); } @@ -272,11 +273,6 @@ export class AppStateService { } } - private _getExistingFiles(projectId: string) { - const found = this._appState.projects.find((p) => p.project.projectId === projectId); - return found ? found.files : []; - } - async reloadActiveFile() { if (!this.activeFile) { return null; @@ -310,64 +306,6 @@ export class AppStateService { return this._processFiles(project, files, emitEvents); } - private _processFiles(project: ProjectWrapper, files: FileStatus[], emitEvents: boolean = true) { - const oldFiles = [...project.files]; - - const fileStatusChangedEvent = []; - const fileReanalysedEvent = []; - - for (const file of files) { - let found = false; - for (const oldFile of oldFiles) { - if (oldFile.fileId === file.fileId) { - // emit when analysis count changed - const fileStatusWrapper = new FileStatusWrapper( - file, - this._userService.getNameForId(file.currentReviewer), - project.ruleSetId, - this._appState.fileAttributesConfig[project.ruleSetId] - ); - if (JSON.stringify(oldFile) !== JSON.stringify(fileStatusWrapper)) { - fileStatusChangedEvent.push(fileStatusWrapper); - } - if (oldFile.lastProcessed !== file.lastProcessed) { - fileReanalysedEvent.push(fileStatusWrapper); - } - found = true; - break; - } - } - // emit for new file - if (!found) { - const fsw = new FileStatusWrapper( - file, - this._userService.getNameForId(file.currentReviewer), - project.ruleSetId, - this._appState.fileAttributesConfig[project.ruleSetId] - ); - fileStatusChangedEvent.push(fsw); - } - } - - project.files = files.map( - (f) => - new FileStatusWrapper( - f, - this._userService.getNameForId(f.currentReviewer), - project.ruleSetId, - this._appState.fileAttributesConfig[project.ruleSetId] - ) - ); - this._computeStats(); - - if (emitEvents) { - fileReanalysedEvent.forEach((file) => this.fileReanalysed.emit(file)); - fileStatusChangedEvent.forEach((file) => this.fileChanged.emit(file)); - } - - return files; - } - async reanalyzeProject(project?: ProjectWrapper) { if (!project) { project = this.activeProject; @@ -467,32 +405,6 @@ export class AppStateService { } } - private _computeStats() { - let totalAnalysedPages = 0; - let totalDocuments = 0; - const totalPeople = new Set(); - this._appState.projects.forEach((p) => { - totalDocuments += p.files.length; - if (p.project.memberIds) { - p.project.memberIds.forEach((m) => totalPeople.add(m)); - } - let numberOfPages = 0; - p.files.forEach((f) => { - numberOfPages += f.numberOfPages; - }); - p.totalNumberOfPages = numberOfPages; - totalAnalysedPages += numberOfPages; - }); - - this._appState.totalPeople = totalPeople.size; - this._appState.totalAnalysedPages = totalAnalysedPages; - this._appState.totalDocuments = totalDocuments; - - if (this.activeProjectId && this.activeFileId) { - this.activateFile(this.activeProjectId, this.activeFileId); - } - } - async reloadActiveProjectFiles() { if (this.activeProjectId) { await this.getFiles(this.activeProject); @@ -684,4 +596,93 @@ export class AppStateService { const ruleSetIds = this.ruleSets.map((rs) => rs.ruleSetId); this._appState.versions = await this._versionsControllerService.getVersions(ruleSetIds).toPromise(); } + + private _getExistingFiles(projectId: string) { + const found = this._appState.projects.find((p) => p.project.projectId === projectId); + return found ? found.files : []; + } + + private _processFiles(project: ProjectWrapper, files: FileStatus[], emitEvents: boolean = true) { + const oldFiles = [...project.files]; + + const fileStatusChangedEvent = []; + const fileReanalysedEvent = []; + + for (const file of files) { + let found = false; + for (const oldFile of oldFiles) { + if (oldFile.fileId === file.fileId) { + // emit when analysis count changed + const fileStatusWrapper = new FileStatusWrapper( + file, + this._userService.getNameForId(file.currentReviewer), + project.ruleSetId, + this._appState.fileAttributesConfig[project.ruleSetId] + ); + if (JSON.stringify(oldFile) !== JSON.stringify(fileStatusWrapper)) { + fileStatusChangedEvent.push(fileStatusWrapper); + } + if (oldFile.lastProcessed !== file.lastProcessed) { + fileReanalysedEvent.push(fileStatusWrapper); + } + found = true; + break; + } + } + // emit for new file + if (!found) { + const fsw = new FileStatusWrapper( + file, + this._userService.getNameForId(file.currentReviewer), + project.ruleSetId, + this._appState.fileAttributesConfig[project.ruleSetId] + ); + fileStatusChangedEvent.push(fsw); + } + } + + project.files = files.map( + (f) => + new FileStatusWrapper( + f, + this._userService.getNameForId(f.currentReviewer), + project.ruleSetId, + this._appState.fileAttributesConfig[project.ruleSetId] + ) + ); + this._computeStats(); + + if (emitEvents) { + fileReanalysedEvent.forEach((file) => this.fileReanalysed.emit(file)); + fileStatusChangedEvent.forEach((file) => this.fileChanged.emit(file)); + } + + return files; + } + + private _computeStats() { + let totalAnalysedPages = 0; + let totalDocuments = 0; + const totalPeople = new Set(); + this._appState.projects.forEach((p) => { + totalDocuments += p.files.length; + if (p.project.memberIds) { + p.project.memberIds.forEach((m) => totalPeople.add(m)); + } + let numberOfPages = 0; + p.files.forEach((f) => { + numberOfPages += f.numberOfPages; + }); + p.totalNumberOfPages = numberOfPages; + totalAnalysedPages += numberOfPages; + }); + + this._appState.totalPeople = totalPeople.size; + this._appState.totalAnalysedPages = totalAnalysedPages; + this._appState.totalDocuments = totalDocuments; + + if (this.activeProjectId && this.activeFileId) { + this.activateFile(this.activeProjectId, this.activeFileId); + } + } } diff --git a/apps/red-ui/src/app/state/model/project.wrapper.ts b/apps/red-ui/src/app/state/model/project.wrapper.ts index 3a09114fe..d1a7913ca 100644 --- a/apps/red-ui/src/app/state/model/project.wrapper.ts +++ b/apps/red-ui/src/app/state/model/project.wrapper.ts @@ -1,4 +1,4 @@ -import { FileStatusWrapper } from '../../models/file/file-status.wrapper'; +import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import * as moment from 'moment'; import { Project } from '@redaction/red-ui-http'; @@ -13,9 +13,18 @@ export class ProjectWrapper { allFilesApproved?: boolean; + constructor(public project: Project, files: FileStatusWrapper[]) { + this._files = files ? files : []; + this._recomputeFileStatus(); + } + private _files: FileStatusWrapper[]; - constructor(public project: Project, files: FileStatusWrapper[]) { + get files() { + return this._files; + } + + set files(files: FileStatusWrapper[]) { this._files = files ? files : []; this._recomputeFileStatus(); } @@ -40,15 +49,6 @@ export class ProjectWrapper { return this.project.projectId; } - set files(files: FileStatusWrapper[]) { - this._files = files ? files : []; - this._recomputeFileStatus(); - } - - get files() { - return this._files; - } - get memberIds() { return this.project.memberIds; } @@ -77,6 +77,10 @@ export class ProjectWrapper { return this.project.projectName; } + get memberCount() { + return this.project.memberIds.length; + } + hasStatus(status: string) { return this._files.find((f) => f.status === status); } @@ -93,10 +97,6 @@ export class ProjectWrapper { return moment(this.projectDate).format('DD/MM/YYYY') === key; } - get memberCount() { - return this.project.memberIds.length; - } - private _recomputeFileStatus() { this.hintsOnly = false; this.hasRedactions = false; diff --git a/apps/red-ui/src/app/utils/api-path-interceptor.ts b/apps/red-ui/src/app/utils/api-path-interceptor.ts index 83d0f4c8e..61d262e22 100644 --- a/apps/red-ui/src/app/utils/api-path-interceptor.ts +++ b/apps/red-ui/src/app/utils/api-path-interceptor.ts @@ -1,7 +1,7 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Inject, Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { AppConfigKey, AppConfigService } from '../modules/app-config/app-config.service'; +import { AppConfigKey, AppConfigService } from '@app-config/app-config.service'; import { BASE_HREF } from '../tokens'; @Injectable() diff --git a/apps/red-ui/src/app/utils/file-drop-utils.ts b/apps/red-ui/src/app/utils/file-drop-utils.ts index c00a605bf..687c439bd 100644 --- a/apps/red-ui/src/app/utils/file-drop-utils.ts +++ b/apps/red-ui/src/app/utils/file-drop-utils.ts @@ -1,5 +1,5 @@ -import { FileUploadModel } from '../modules/upload-download/model/file-upload.model'; -import { ProjectWrapper } from '../state/model/project.wrapper'; +import { FileUploadModel } from '@upload-download/model/file-upload.model'; +import { ProjectWrapper } from '@state/model/project.wrapper'; export function handleFileDrop(event: DragEvent, project: ProjectWrapper, uploadFiles: (files: FileUploadModel[]) => void) { event.preventDefault(); diff --git a/apps/red-ui/src/app/utils/uuid-helper.ts b/apps/red-ui/src/app/utils/uuid-helper.ts index 2f7d426a2..3cd05d407 100644 --- a/apps/red-ui/src/app/utils/uuid-helper.ts +++ b/apps/red-ui/src/app/utils/uuid-helper.ts @@ -36,4 +36,5 @@ export function UUID() { lut[(d3 >> 24) & 0xff] ); } + /* eslint-enable */ diff --git a/tsconfig.base.json b/tsconfig.base.json index d8d55a1c4..31fcd116e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -18,7 +18,18 @@ "baseUrl": ".", "paths": { "@redaction/red-ui-http": ["libs/red-ui-http/src/index.ts"], - "@redaction/red-cache": ["libs/red-cache/src/index.ts"] + "@redaction/red-cache": ["libs/red-cache/src/index.ts"], + "@services/*": ["apps/red-ui/src/app/services/*"], + "@components/*": ["apps/red-ui/src/app/components/*"], + "@guards/*": ["apps/red-ui/src/app/guards/*"], + "@i18n/*": ["apps/red-ui/src/app/i18n/*"], + "@state/*": ["apps/red-ui/src/app/state/*"], + "@utils/*": ["apps/red-ui/src/app/utils/*"], + "@models/*": ["apps/red-ui/src/app/models/*"], + "@environments/*": ["apps/red-ui/src/environments/*"], + "@shared/*": ["apps/red-ui/src/app/modules/shared/*"], + "@app-config/*": ["apps/red-ui/src/app/modules/app-config/*"], + "@upload-download/*": ["apps/red-ui/src/app/modules/upload-download/*"] } }, "exclude": ["node_modules", "tmp"],