update paths & refactor imports

This commit is contained in:
Dan Percic 2021-05-10 09:04:34 +03:00
parent f46dacc380
commit 7b31e4c59c
118 changed files with 1617 additions and 1589 deletions

View File

@ -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 = [
{

View File

@ -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',

View File

@ -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';

View File

@ -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',

View File

@ -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;
}

View File

@ -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);
}

View File

@ -1,12 +1,12 @@
<redaction-circle-button [matMenuTriggerFor]="overlay" icon="red:notification" [showDot]="hasUnread"></redaction-circle-button>
<mat-menu #overlay="matMenu" class="notifications-menu" backdropClass="notifications-backdrop" xPosition="before">
<redaction-circle-button [matMenuTriggerFor]="overlay" [showDot]="hasUnread" icon="red:notification"></redaction-circle-button>
<mat-menu #overlay="matMenu" backdropClass="notifications-backdrop" class="notifications-menu" xPosition="before">
<div *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
<div class="all-caps-label">{{ day(group) }}</div>
<div
class="notification"
mat-menu-item
*ngFor="let notification of group.notifications | sortBy: 'desc':'eventTime'"
[class.unread]="!notification.read"
class="notification"
mat-menu-item
>
<redaction-initials-avatar></redaction-initials-avatar>
<div class="notification-content">
@ -14,9 +14,9 @@
<div class="small-label mt-2">{{ eventTime(notification.eventTime) }}</div>
</div>
<div
class="dot"
(click)="toggleRead(notification, $event)"
[matTooltip]="(notification.read ? 'notifications.mark-unread' : 'notifications.mark-read') | translate"
class="dot"
matTooltipPosition="before"
></div>
</div>

View File

@ -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 <a>link</a>', eventTime: 1607254981000, read: true },
{ message: 'This is a <b>notification 1</b>', 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] });
}
}
}

View File

@ -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<void> {
this.viewReady = false;

View File

@ -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'

View File

@ -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 (
<any>this.canMarkTextOnlyAsFalsePositive +
<any>this.canMarkAsFalsePositive +
<any>this.canRemoveOrSuggestToRemoveFromDictionary +
<any>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 (
<any>this.canMarkTextOnlyAsFalsePositive +
<any>this.canMarkAsFalsePositive +
<any>this.canRemoveOrSuggestToRemoveFromDictionary +
<any>this.canRemoveOrSuggestToRemoveOnlyHere >=
2
);
}
}

View File

@ -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';

View File

@ -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;

View File

@ -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<number>;

View File

@ -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';

View File

@ -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';

View File

@ -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',

View File

@ -21,6 +21,7 @@
&:focus {
outline: none;
}
&.hidden {
display: none;
}

View File

@ -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

View File

@ -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';

View File

@ -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',

View File

@ -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',

View File

@ -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({

View File

@ -7,7 +7,7 @@
<div class="dialog-content">
<div class="red-input-group required w-300">
<label translate="add-edit-file-attribute.form.name"></label>
<input formControlName="label" name="label" type="text" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" />
<input formControlName="label" name="label" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" type="text" />
</div>
<div class="red-input-group required w-300">
@ -15,8 +15,8 @@
<input
formControlName="csvColumnHeader"
name="csvColumnHeader"
type="text"
placeholder="{{ 'add-edit-file-attribute.form.column-header-placeholder' | translate }}"
type="text"
/>
</div>
@ -31,11 +31,11 @@
<div class="options-wrapper">
<div class="red-input-group">
<mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
<mat-slide-toggle color="primary" formControlName="readonly">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
</div>
<div class="red-input-group mt-0">
<mat-checkbox name="primaryAttribute" formControlName="primaryAttribute" color="primary">
<mat-checkbox color="primary" formControlName="primaryAttribute" name="primaryAttribute">
{{ 'add-edit-file-attribute.form.primary' | translate }}
</mat-checkbox>
</div>
@ -48,5 +48,5 @@
</div>
</form>
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
</section>

View File

@ -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';

View File

@ -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;
};
}
}

View File

@ -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();
}
});
}
}
}

View File

@ -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();
}

View File

@ -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';
}
}

View File

@ -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,

View File

@ -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';

View File

@ -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<Field> {
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
};
}
}

View File

@ -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({

View File

@ -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);
}
}

View File

@ -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();
});
}
}

View File

@ -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<TypeValue> 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<TypeV
this._loadDictionaryData();
}
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();
});
}
private _loadDictionaryData() {
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
this.allEntities = Object.keys(appStateDictionaryData)
@ -71,21 +87,4 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
this.chartData.sort((a, b) => (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();
});
}
}

View File

@ -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';

View File

@ -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() {}
}

View File

@ -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<FileAttributeConfig> 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<F
await this._loadData();
}
private async _loadData() {
try {
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
this._existingConfiguration = response;
this.allEntities = response?.fileAttributeConfigs || [];
} catch (e) {
} finally {
this._executeSearchImmediately();
this.viewReady = true;
this.loading = false;
}
}
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
$event.stopPropagation();
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async (newValue: FileAttributeConfig) => {
@ -82,4 +67,17 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
await this._loadData();
});
}
private async _loadData() {
try {
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
this._existingConfiguration = response;
this.allEntities = response?.fileAttributeConfigs || [];
} catch (e) {
} finally {
this._executeSearchImmediately();
this.viewReady = true;
this.loading = false;
}
}
}

View File

@ -1,7 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { PermissionsService } from '../../../../services/permissions.service';
import { PermissionsService } from '@services/permissions.service';
import { LicenseReport, LicenseReportControllerService } from '@redaction/red-ui-http';
import { AppConfigService } from '../../../app-config/app-config.service';
import { AppConfigService } from '@app-config/app-config.service';
import * as moment from 'moment';
import { TranslateService } from '@ngx-translate/core';
@ -11,6 +11,27 @@ import { TranslateService } from '@ngx-translate/core';
styleUrls: ['./license-information-screen.component.scss']
})
export class LicenseInformationScreenComponent implements OnInit {
currentInfo: LicenseReport = {};
totalInfo: LicenseReport = {};
unlicensedInfo: LicenseReport = {};
totalLicensedNumberOfPages = 0;
analysisPercentageOfLicense = 100;
viewReady = false;
barChart: any[] = [];
lineChartSeries: any[] = [];
yAxisLabel = this._translateService.instant('license-info-screen.chart.pages-per-month');
yAxisLabelRight = this._translateService.instant('license-info-screen.chart.total-pages');
lineChartScheme = {
selectable: true,
group: 'Ordinal',
domain: ['#dd4d50', '#5ce594', '#0389ec']
};
comboBarScheme = {
selectable: true,
group: 'Ordinal',
domain: ['#0389ec']
};
constructor(
readonly permissionsService: PermissionsService,
readonly appConfigService: AppConfigService,
@ -21,30 +42,6 @@ export class LicenseInformationScreenComponent implements OnInit {
get currentYear(): number {
return new Date().getFullYear();
}
currentInfo: LicenseReport = {};
totalInfo: LicenseReport = {};
unlicensedInfo: LicenseReport = {};
totalLicensedNumberOfPages = 0;
analysisPercentageOfLicense = 100;
viewReady = false;
barChart: any[] = [];
lineChartSeries: any[] = [];
yAxisLabel = this._translateService.instant('license-info-screen.chart.pages-per-month');
yAxisLabelRight = this._translateService.instant('license-info-screen.chart.total-pages');
lineChartScheme = {
selectable: true,
group: 'Ordinal',
domain: ['#dd4d50', '#5ce594', '#0389ec']
};
comboBarScheme = {
selectable: true,
group: 'Ordinal',
domain: ['#0389ec']
};
async ngOnInit() {
this.totalLicensedNumberOfPages = this.appConfigService.getConfig('LICENSE_PAGE_COUNT', 0);
@ -74,6 +71,16 @@ export class LicenseInformationScreenComponent implements OnInit {
});
}
sendMail(): void {
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer });
const body = [
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
].join('%0D%0A');
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
}
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
const [startMonth, startYear] = [startDate.month(), startDate.year()];
const [endMonth, endYear] = [endDate.month(), endDate.year()];
@ -144,14 +151,4 @@ export class LicenseInformationScreenComponent implements OnInit {
}
];
}
sendMail(): void {
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer });
const body = [
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
].join('%0D%0A');
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
}
}

View File

@ -1,9 +1,9 @@
import { Component, Injector, OnInit } from '@angular/core';
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';
import { PermissionsService } from '@services/permissions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { RuleSetModel } from '@redaction/red-ui-http';
@Component({
@ -37,6 +37,14 @@ export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSet
this._loadRuleSetStats();
}
openAddRuleSetDialog() {
this._dialogService.openAddEditRuleSetDialog(null, async (newRuleSet) => {
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<RuleSet
}
});
}
openAddRuleSetDialog() {
this._dialogService.openAddEditRuleSetDialog(null, async (newRuleSet) => {
if (newRuleSet) {
this.loadRuleSetsData();
}
});
}
}

View File

@ -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<void> {
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;
}
);
}
}

View File

@ -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;
}
}
}

View File

@ -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<User> 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<User> 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<User> 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<User> 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;
}
}

View File

@ -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':

View File

@ -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';

View File

@ -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({

View File

@ -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(() => {

View File

@ -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({

View File

@ -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]);

View File

@ -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({

View File

@ -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<any>) {
this.loading = true;
obs.subscribe().add(() => {

View File

@ -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',

View File

@ -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);
}
}

View File

@ -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';
}
}

View File

@ -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<any>;
@Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
@Output() selectAnnotations = new EventEmitter<AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }>();
@Output() deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
@Output() selectPage = new EventEmitter<number>();
@Output() toggleSkipped = new EventEmitter<any>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
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;
}
}

View File

@ -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 && (<any>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) {

View File

@ -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);
});
}
}

View File

@ -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<string[]>();
@Output() manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
@ -38,9 +33,11 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Output() keyUp = new EventEmitter<KeyboardEvent>();
@Output() viewerReady = new EventEmitter<WebViewerInstance>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
@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');

View File

@ -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;

View File

@ -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({

View File

@ -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',

View File

@ -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;
}

View File

@ -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',

View File

@ -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);
}

View File

@ -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
};
}
}

View File

@ -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<string>();
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<string>();
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;
}
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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(

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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,

View File

@ -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<ProjectWrapper> 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<ProjectW
this._loadEntitiesFromState();
}
get noData() {
return this.allEntities.length === 0;
}
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;
}
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<ProjectW
this._fileChangedSub.unsubscribe();
}
private _loadEntitiesFromState() {
this.allEntities = this._appStateService.allProjects;
}
get noData() {
return this.allEntities.length === 0;
}
protected get _filterComponents(): FilterComponent[] {
return [this._statusFilterComponent, this._peopleFilterComponent, this._needsWorkFilterComponent, this._ruleSetFilterComponent];
}
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);
}
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<ProjectW
this._dialogService.openAssignProjectMembersAndOwnerDialog($event, project);
}
actionPerformed() {
this._calculateData();
}
protected _preFilter() {
this.detailsContainerFilters = {
statusFilters: this.statusFilters.map((f) => ({ ...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<string>();
const allDistinctPeople = new Set<string>();
@ -260,28 +284,4 @@ export class ProjectListingScreenComponent extends BaseListingComponent<ProjectW
});
this.ruleSetFilters = processFilters(this.ruleSetFilters, ruleSetFilters);
}
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 }
];
}
protected _preFilter() {
this.detailsContainerFilters = {
statusFilters: this.statusFilters.map((f) => ({ ...f }))
};
}
actionPerformed() {
this._calculateData();
}
}

View File

@ -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<FileStatusWrapper> 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<FileSta
this._loadEntitiesFromState();
}
get activeProject(): ProjectWrapper {
return this._appStateService.activeProject;
}
protected get _filterComponents(): FilterComponent[] {
return [this._statusFilterComponent, this._peopleFilterComponent, this._needsWorkFilterComponent];
}
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
}
];
}
ngOnInit(): void {
this._fileDropOverlayService.initFileDropHandling();
@ -121,10 +139,6 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
this.ngOnDestroy();
}
get activeProject(): ProjectWrapper {
return this._appStateService.activeProject;
}
reanalyseProject() {
return this._appStateService
.reanalyzeProject()
@ -157,14 +171,6 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
return [FileStatus.StatusEnum.REPROCESS, FileStatus.StatusEnum.FULLREPROCESS, FileStatus.StatusEnum.PROCESSING].includes(fileStatusWrapper.status);
}
protected get _filterComponents(): FilterComponent[] {
return [this._statusFilterComponent, this._peopleFilterComponent, this._needsWorkFilterComponent];
}
private _loadEntitiesFromState() {
if (this.activeProject) this.allEntities = this.activeProject.files;
}
reloadProjects() {
this._appStateService.getFiles(this._appStateService.activeProject, false).then(() => {
this.calculateData();
@ -201,6 +207,46 @@ export class ProjectOverviewScreenComponent extends BaseListingComponent<FileSta
await this._uploadFiles(convertFiles(files, this.activeProject));
}
fileLink(fileStatus: FileStatusWrapper) {
return this.permissionsService.canOpenFile(fileStatus) ? ['/main/projects/' + this.activeProject.project.projectId + '/file/' + fileStatus.fileId] : [];
}
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;
}
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<FileSta
needsWorkFilters.sort((a, b) => 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;
}
}

View File

@ -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<any>, annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>) {
obs.subscribe(
() => {
annotationsChanged.emit(annotation);
},
() => {
annotationsChanged.emit();
}
);
}
getViewerAvailableActions(annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>): Record<string, unknown>[] {
const availableActions = [];
@ -233,6 +222,17 @@ export class AnnotationActionsService {
return availableActions;
}
private _processObsAndEmit(obs: Observable<any>, annotation: AnnotationWrapper, annotationsChanged: EventEmitter<AnnotationWrapper>) {
obs.subscribe(
() => {
annotationsChanged.emit(annotation);
},
() => {
annotationsChanged.emit();
}
);
}
private _getFalsePositiveText(annotation: AnnotationWrapper) {
if (annotation.canBeMarkedAsFalsePositive) {
let text;

View File

@ -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);
}
}

View File

@ -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<string, FilterModel>();
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<string, FilterModel>, 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<string, FilterModel>, 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
}
];
}
}

View File

@ -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();
}
}
}

View File

@ -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,

View File

@ -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 {

View File

@ -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';

View File

@ -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<T = any> {
protected readonly _selectionKey: string;
protected readonly _sortKey: ScreenName;
protected constructor(protected readonly _injector: Injector) {
this._formBuilder = this._injector.get<FormBuilder>(FormBuilder);
this._changeDetectorRef = this._injector.get<ChangeDetectorRef>(ChangeDetectorRef);
this._sortingService = this._injector.get<SortingService>(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<T = any> {
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>(FormBuilder);
this._changeDetectorRef = this._injector.get<ChangeDetectorRef>(ChangeDetectorRef);
this._sortingService = this._injector.get<SortingService>(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<T = any> {
}
}
// 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<T = any> {
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());
}
}

View File

@ -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);
}
}

View File

@ -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';

View File

@ -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',

View File

@ -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',

View File

@ -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({

View File

@ -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;
});
});
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -8,10 +8,21 @@ const DISPLAYED_ITEMS = 5;
styleUrls: ['./pagination.component.scss']
})
export class PaginationComponent {
displayedPages: (number | string)[];
@Output() pageChanged = new EventEmitter<number>();
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<number>();
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;
}
}

View File

@ -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: '' });
}
}

View File

@ -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;

View File

@ -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',

View File

@ -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',

View File

@ -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;
}
}

View File

@ -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]'

Some files were not shown because too many files have changed in this diff Show More