Merge branch 'master' into workflow

This commit is contained in:
Adina Țeudan 2021-09-27 22:42:15 +03:00
commit aeb804b591
164 changed files with 4606 additions and 3951 deletions

2
.gitignore vendored
View File

@ -41,3 +41,5 @@ Thumbs.db
version.properties
paligo-styles/style.css*
migrations.json

View File

@ -19,6 +19,57 @@
"@nrwl/angular:component": {}
},
"projects": {
"common-ui": {
"projectType": "library",
"root": "libs/common-ui",
"sourceRoot": "libs/common-ui/src",
"prefix": "redaction",
"architect": {
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/common-ui"],
"options": {
"jestConfig": "libs/common-ui/jest.config.js",
"passWithNoTests": true
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/common-ui/src/**/*.ts", "libs/common-ui/src/**/*.html"]
},
"outputs": ["{options.outputFile}"]
}
}
},
"red-cache": {
"projectType": "library",
"root": "libs/red-cache",
"sourceRoot": "libs/red-cache/src",
"prefix": "redaction",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/red-cache/src/**/*.ts", "libs/red-cache/src/**/*.html"]
},
"outputs": ["{options.outputFile}"]
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/red-cache/jest.config.js",
"passWithNoTests": true
},
"outputs": ["coverage/libs/red-cache"]
}
},
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
}
},
"red-ui": {
"projectType": "application",
"schematics": {
@ -38,6 +89,7 @@
"main": "apps/red-ui/src/main.ts",
"polyfills": "apps/red-ui/src/polyfills.ts",
"tsConfig": "apps/red-ui/tsconfig.app.json",
"baseHref": "/ui/",
"assets": [
"apps/red-ui/src/favicon.ico",
{
@ -57,7 +109,10 @@
},
"apps/red-ui/src/manifest.webmanifest"
],
"styles": ["apps/red-ui/src/styles.scss", "libs/common-ui/src/assets/styles/common.scss"],
"styles": ["apps/red-ui/src/styles.scss", "libs/common-ui/src/assets/styles/common-styles.scss"],
"stylePreprocessorOptions": {
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
},
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js"],
"vendorChunk": true,
"extractLicenses": false,
@ -120,7 +175,8 @@
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["apps/red-ui/src/**/*.ts", "apps/red-ui/src/**/*.html"]
}
},
"outputs": ["{options.outputFile}"]
},
"test": {
"builder": "@nrwl/jest:jest",
@ -149,7 +205,8 @@
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/red-ui-http/src/**/*.ts", "libs/red-ui-http/src/**/*.html"]
}
},
"outputs": ["{options.outputFile}"]
},
"test": {
"builder": "@nrwl/jest:jest",
@ -165,55 +222,6 @@
"style": "scss"
}
}
},
"red-cache": {
"projectType": "library",
"root": "libs/red-cache",
"sourceRoot": "libs/red-cache/src",
"prefix": "redaction",
"architect": {
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/red-cache/src/**/*.ts", "libs/red-cache/src/**/*.html"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/red-cache/jest.config.js",
"passWithNoTests": true
},
"outputs": ["coverage/libs/red-cache"]
}
},
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
}
},
"common-ui": {
"projectType": "library",
"root": "libs/common-ui",
"sourceRoot": "libs/common-ui/src",
"prefix": "redaction",
"architect": {
"test": {
"builder": "@nrwl/jest:jest",
"outputs": ["coverage/libs/common-ui"],
"options": {
"jestConfig": "libs/common-ui/jest.config.js",
"passWithNoTests": true
}
},
"lint": {
"builder": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": ["libs/common-ui/src/**/*.ts", "libs/common-ui/src/**/*.html"]
}
}
}
}
}
}

View File

@ -4,9 +4,7 @@ module.exports = {
globals: {
'ts-jest': {
stringifyContentPathRegex: '\\.(html|svg)$',
astTransformers: {
before: ['jest-preset-angular/build/InlineFilesTransformer', 'jest-preset-angular/build/StripStylesTransformer']
},
tsconfig: '<rootDir>/tsconfig.spec.json'
}
},
@ -17,5 +15,6 @@ module.exports = {
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment'
]
],
transform: { '^.+\\.(ts|js|html)$': 'jest-preset-angular' }
};

View File

@ -30,16 +30,16 @@ import { MONACO_PATH, MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
import { GlobalErrorHandler } from '@utils/global-error-handler.service';
import { REDMissingTranslationHandler } from '@utils/missing-translations-handler';
import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler';
import { configurationInitializer } from '@app-config/configuration.initializer';
import { AppConfigService } from '@app-config/app-config.service';
import { configurationInitializer } from '@utils/configuration.initializer';
import { ConfigService } from '@services/config.service';
import { SpotlightSearchComponent } from '@components/spotlight-search/spotlight-search.component';
import { PruningTranslationLoader } from '@utils/pruning-translation-loader';
import { DatePipe } from '@shared/pipes/date.pipe';
import * as links from '../assets/help-mode/links.json';
import { HELP_DOCS, IqserHelpModeModule } from '@iqser/common-ui';
import { ServerErrorInterceptor } from '@utils/server-error-interceptor';
import { HELP_DOCS, IqserHelpModeModule, MAX_RETRIES_ON_SERVER_ERROR, ServerErrorInterceptor } from '@iqser/common-ui';
import { KeycloakService } from 'keycloak-angular';
export function httpLoaderFactory(httpClient: HttpClient) {
export function httpLoaderFactory(httpClient: HttpClient): PruningTranslationLoader {
return new PruningTranslationLoader(httpClient, '/assets/i18n/', '.json');
}
@ -131,7 +131,7 @@ const components = [
provide: APP_INITIALIZER,
multi: true,
useFactory: configurationInitializer,
deps: [Title, AppConfigService, GeneralSettingsControllerService]
deps: [KeycloakService, Title, ConfigService, GeneralSettingsControllerService]
},
{
provide: MONACO_PATH,
@ -145,6 +145,11 @@ const components = [
provide: HELP_DOCS,
useValue: links
},
{
provide: MAX_RETRIES_ON_SERVER_ERROR,
useFactory: (configService: ConfigService) => configService.values.MAX_RETRIES_ON_SERVER_ERROR,
deps: [ConfigService]
},
DatePipe
],
bootstrap: [AppComponent]

View File

@ -1,19 +1,19 @@
<section>
<p *ngIf="!configuredAdminName && !configuredAdminUrl" class="heading-xl" translate="auth-error.heading"></p>
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
<p
*ngIf="configuredAdminName && configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }"
*ngIf="adminName && adminUrl"
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: adminName, adminUrl: adminUrl }"
class="heading-xl"
></p>
<p
*ngIf="configuredAdminName && !configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: configuredAdminName }"
*ngIf="adminName && !adminUrl"
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: adminName }"
class="heading-xl"
></p>
<p
*ngIf="!configuredAdminName && configuredAdminUrl"
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: configuredAdminName }"
*ngIf="!adminName && adminUrl"
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: adminName }"
class="heading-xl"
></p>
<a (click)="logout()" translate="auth-error.logout"></a>
<a (click)="userService.logout()" translate="auth-error.logout"></a>
</section>

View File

@ -1,24 +1,15 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { UserService } from '@services/user.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
@Component({
selector: 'redaction-auth-error',
templateUrl: './auth-error.component.html',
styleUrls: ['./auth-error.component.scss']
})
export class AuthErrorComponent implements OnInit {
configuredAdminName: string;
configuredAdminUrl: string;
export class AuthErrorComponent {
adminName = this._configService.values.ADMIN_CONTACT_NAME;
adminUrl = this._configService.values.ADMIN_CONTACT_URL;
constructor(private readonly _userService: UserService, private readonly _appConfigService: AppConfigService) {}
ngOnInit(): void {
this.configuredAdminName = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_NAME);
this.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
}
logout() {
this._userService.logout();
}
constructor(readonly userService: UserService, private readonly _configService: ConfigService) {}
}

View File

@ -1,8 +1,8 @@
@import '../../../assets/styles/variables';
@use 'variables';
.dev-mode {
background-color: $primary;
color: $white;
background-color: variables.$primary;
color: variables.$white;
font-size: 22px;
line-height: 16px;
text-align: center;

View File

@ -1,7 +1,7 @@
@import '../../../assets/styles/variables';
@use 'variables';
:host {
color: $primary;
color: variables.$primary;
}
mat-icon {

View File

@ -1,4 +1,4 @@
@import '../../../assets/styles/variables';
@use 'variables';
.mt-2 {
margin-top: 2px;
@ -35,7 +35,7 @@
white-space: normal;
a {
color: $accent;
color: variables.$accent;
font-weight: bold;
}
}
@ -45,17 +45,17 @@
height: 8px;
width: 8px;
border-radius: 50%;
background-color: $grey-4;
background-color: variables.$grey-4;
position: absolute;
top: 8px;
right: 8px;
}
&:hover {
background-color: $grey-6;
background-color: variables.$grey-6;
.dot {
background-color: $grey-5;
background-color: variables.$grey-5;
}
a {
@ -64,10 +64,10 @@
}
&.unread {
background-color: rgba($primary, 0.1);
background-color: rgba(variables.$primary, 0.1);
.dot {
background-color: $primary;
background-color: variables.$primary;
}
}
}

View File

@ -1,4 +1,4 @@
@import '../../../assets/styles/variables';
@use 'variables';
.spotlight-row {
width: 300px;
@ -9,13 +9,13 @@
font-size: 13px;
border: none;
outline: none;
color: $accent;
background-color: $white;
color: variables.$accent;
background-color: variables.$white;
}
.highlight {
border-radius: 4px;
background-color: $grey-2;
background-color: variables.$grey-2;
}
.wrapper {

View File

@ -1 +0,0 @@
@import '../../../assets/styles/variables';

View File

@ -1,10 +1,10 @@
@import '../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.content-container {
background-color: $grey-2;
background-color: variables.$grey-2;
justify-content: center;
@include scroll-bar;
@include common-mixins.scroll-bar;
overflow: auto;
}

View File

@ -5,7 +5,7 @@ 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';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { DomSanitizer } from '@angular/platform-browser';
import { languagesTranslations } from '../../translations/languages-translations';
import { LoadingService } from '@iqser/common-ui';
@ -26,7 +26,7 @@ export class UserProfileScreenComponent implements OnInit {
readonly permissionsService: PermissionsService,
private readonly _formBuilder: FormBuilder,
private readonly _userService: UserService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _userControllerService: UserControllerService,
private readonly _languageService: LanguageService,
private readonly _domSanitizer: DomSanitizer,
@ -42,7 +42,7 @@ export class UserProfileScreenComponent implements OnInit {
});
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
this._appConfigService.getConfig(AppConfigKey.OAUTH_URL) + '/account/password'
this._configService.values.OAUTH_URL + '/account/password'
);
}

View File

@ -57,11 +57,10 @@ export class AnnotationWrapper {
isChangeLogEntry?: boolean;
changeLogType?: 'ADDED' | 'REMOVED';
engines?: string[];
private _origin: RedactionLogEntryWrapper;
constructor() {}
get isChangeLogRemoved() {
return this.changeLogType === 'REMOVED';
}
@ -220,6 +219,7 @@ export class AnnotationWrapper {
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
annotationWrapper.comments = redactionLogEntry.comments || [];
annotationWrapper.manual = redactionLogEntry.manual;
annotationWrapper.engines = redactionLogEntry.engines;
this._createContent(annotationWrapper, redactionLogEntry);
this._setSuperType(annotationWrapper, redactionLogEntry);

View File

@ -35,4 +35,5 @@ export interface RedactionLogEntryWrapper {
recategorizationType?: string;
legalBasisChangeValue?: string;
engines?: string[];
}

View File

@ -1,5 +1,3 @@
@import '../../../../../assets/styles/variables';
.first-row {
display: flex;

View File

@ -1,4 +1,4 @@
<div [translateParams]="{ userName: userName }" [translate]="'reset-password-dialog.header'" class="dialog-header heading-l"></div>
<div [translateParams]="{ userName: user | name }" [translate]="'reset-password-dialog.header'" class="dialog-header heading-l"></div>
<form (submit)="save()" [formGroup]="passwordForm">
<div class="dialog-content">

View File

@ -23,10 +23,6 @@ export class ResetPasswordComponent {
private readonly _loadingService: LoadingService
) {}
get userName() {
return this._userService.getNameForId(this.user.id);
}
async save() {
this._loadingService.start();
await this._userControllerService

View File

@ -1,7 +1,7 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.dialog-header {
color: $primary;
color: variables.$primary;
}
.heading {

View File

@ -1,7 +1,7 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.dialog-header {
color: $primary;
color: variables.$primary;
}
.heading {

View File

@ -1,4 +1,4 @@
@import '../../../../../../assets/styles/variables';
@use 'variables';
:host ::ng-deep iqser-table {
iqser-table-header {
@ -8,7 +8,7 @@
.header-item {
box-shadow: none;
border-top: 1px solid $separator;
border-top: 1px solid variables.$separator;
.all-caps-label {
margin-right: 10px;
@ -20,7 +20,7 @@
.separator {
margin-left: 14px;
background-color: $separator;
background-color: variables.$separator;
width: 1px;
height: 30px;
margin-right: 16px;

View File

@ -1,5 +1,5 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.sub-header {
display: flex;
@ -13,7 +13,7 @@
padding-left: 32px;
min-width: 376px;
box-sizing: border-box;
border-right: 1px solid $separator;
border-right: 1px solid variables.$separator;
.info {
margin-bottom: 8px;
@ -55,9 +55,9 @@
.csv-part-header {
height: 50px;
box-sizing: border-box;
background: $white;
border-top: 1px solid $separator;
border-bottom: 1px solid $separator;
background: variables.$white;
border-top: 1px solid variables.$separator;
border-bottom: 1px solid variables.$separator;
display: flex;
align-items: center;
justify-content: space-between;
@ -83,14 +83,14 @@
}
.search-input-container {
background-color: $white;
border-bottom: 1px solid $separator;
background-color: variables.$white;
border-bottom: 1px solid variables.$separator;
padding: 8px 16px;
}
.csv-part-content {
overflow: auto;
@include no-scroll-bar;
@include common-mixins.no-scroll-bar;
height: calc(100% - 49px);
font-size: 12px;
@ -101,13 +101,13 @@
align-items: center;
justify-content: center;
text-align: center;
color: $grey-7;
color: variables.$grey-7;
line-height: 15px;
font-weight: 500;
}
> *:not(.no-column-data) {
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
display: flex;
padding: 7px 16px;
align-items: center;
@ -117,14 +117,14 @@
> .left {
width: 375px;
min-width: 375px;
background: $grey-2;
background: variables.$grey-2;
.csv-header-pill-content {
overflow: auto;
padding: 7px 0;
background: $grey-2;
background: variables.$grey-2;
height: calc(100% - 64px);
@include no-scroll-bar;
@include common-mixins.no-scroll-bar;
&.search-open {
height: calc(100% - 58px - 51px);
@ -137,7 +137,7 @@
min-height: 32px;
border-radius: 8px;
padding: 10px;
background: $white;
background: variables.$white;
cursor: pointer;
display: flex;
flex-direction: column;
@ -153,26 +153,26 @@
}
.sample {
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
}
&.selected {
background-color: $primary;
color: $white;
background-color: variables.$primary;
color: variables.$white;
}
}
}
}
border-right: 1px solid $separator;
border-right: 1px solid variables.$separator;
}
> .center {
width: 150px;
min-width: 150px;
background: $grey-2;
border-right: 1px solid $separator;
background: variables.$grey-2;
border-right: 1px solid variables.$separator;
&:not(.collapsed) iqser-circle-button {
margin-right: -8px;

View File

@ -1,4 +1,4 @@
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'common-mixins';
:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell {
&.center {
@ -6,7 +6,7 @@
}
&.label span {
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
&.read-only mat-icon {

View File

@ -1,12 +1,12 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.content-container {
background-color: $grey-2;
background-color: variables.$grey-2;
display: flex;
flex-direction: column;
align-items: center;
@include scroll-bar;
@include common-mixins.scroll-bar;
overflow: auto;
}
@ -16,7 +16,7 @@
}
.inline-input-group {
background-color: rgba($grey-6, 0.8);
background-color: rgba(variables.$grey-6, 0.8);
width: 100%;
height: 38px;
border-radius: 4px;

View File

@ -7,7 +7,7 @@ import {
SmtpConfigurationControllerService,
SMTPConfigurationModel
} from '@redaction/red-ui-http';
import { AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { AutoUnsubscribe, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
@ -33,7 +33,7 @@ export class GeneralConfigScreenComponent extends AutoUnsubscribe implements OnI
private readonly _formBuilder: FormBuilder,
private readonly _loadingService: LoadingService,
private readonly _dialogService: AdminDialogService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _smtpConfigService: SmtpConfigurationControllerService,
private readonly _generalSettingsControllerService: GeneralSettingsControllerService
) {
@ -112,7 +112,7 @@ export class GeneralConfigScreenComponent extends AutoUnsubscribe implements OnI
await this._generalSettingsControllerService.updateGeneralConfigurations(configFormValues).toPromise();
this._initialGeneralConfiguration = await this._generalSettingsControllerService.getGeneralConfigurations().toPromise();
this._appConfigService.updateDisplayName(this._initialGeneralConfiguration.displayName);
this._configService.updateDisplayName(this._initialGeneralConfiguration.displayName);
this._loadingService.stop();
}

View File

@ -16,17 +16,17 @@
<div class="grid-container">
<div class="row">
<div translate="license-info-screen.backend-version"></div>
<div>{{ appConfigService.getConfig('BACKEND_APP_VERSION', '-') }}</div>
<div>{{ configService.values.BACKEND_APP_VERSION || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.frontend-version"></div>
<div>{{ appConfigService.getConfig('FRONTEND_APP_VERSION', '-') }}</div>
<div>{{ configService.values.FRONTEND_APP_VERSION || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.custom-app-title"></div>
<div>{{ appConfigService.getConfig('APP_NAME', '-') }}</div>
<div>{{ configService.values.APP_NAME || '-' }}</div>
</div>
<div class="row">
@ -45,14 +45,14 @@
<div class="row">
<div translate="license-info-screen.licensed-to"></div>
<div>{{ appConfigService.getConfig('LICENSE_CUSTOMER', '-') }}</div>
<div>{{ configService.values.LICENSE_CUSTOMER || '-' }}</div>
</div>
<div class="row">
<div translate="license-info-screen.licensing-period"></div>
<div>
{{ appConfigService.getConfig('LICENSE_START', '-') }} /
{{ appConfigService.getConfig('LICENSE_END', '-') }}
{{ configService.values.LICENSE_START || '-' }} /
{{ configService.values.LICENSE_END || '-' }}
</div>
</div>
@ -107,8 +107,7 @@
[yAxisLabelRight]="'license-info-screen.chart.total-pages' | translate"
[yAxisLabel]="'license-info-screen.chart.pages-per-month' | translate"
[yAxis]="true"
>
</combo-chart-component>
></combo-chart-component>
</div>
</div>
</div>

View File

@ -1,9 +1,9 @@
@import 'libs/common-ui/src/assets/styles/mixins';
@import '../../../../../assets/styles/variables';
@use 'common-mixins';
@use 'variables';
.content-container {
overflow: auto;
@include scroll-bar;
@include common-mixins.scroll-bar;
display: flex;
flex-direction: column;
@ -28,7 +28,7 @@
&:hover {
> div {
background-color: $grey-2;
background-color: variables.$grey-2;
}
}
}
@ -37,7 +37,7 @@
grid-column: span 2;
padding: 20px 20px 8px;
margin-bottom: 8px;
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
}
}

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { LicenseReport, LicenseReportControllerService } from '@redaction/red-ui-http';
import { AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import * as moment from 'moment';
import { TranslateService } from '@ngx-translate/core';
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
@ -29,10 +29,8 @@ export class LicenseInformationScreenComponent implements OnInit {
unlicensedInfo: LicenseReport = {};
totalLicensedNumberOfPages = 0;
analysisPercentageOfLicense = 100;
barChart: any[] = [];
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',
@ -45,8 +43,8 @@ export class LicenseInformationScreenComponent implements OnInit {
};
constructor(
readonly configService: ConfigService,
private readonly _userService: UserService,
readonly appConfigService: AppConfigService,
private readonly _loadingService: LoadingService,
readonly routerHistoryService: RouterHistoryService,
private readonly _translateService: TranslateService,
@ -60,9 +58,9 @@ export class LicenseInformationScreenComponent implements OnInit {
}
async ngOnInit() {
this.totalLicensedNumberOfPages = this.appConfigService.getConfig('LICENSE_PAGE_COUNT', 0);
const startDate = moment(this.appConfigService.getConfig('LICENSE_START'), 'DD-MM-YYYY');
const endDate = moment(this.appConfigService.getConfig('LICENSE_END'), 'DD-MM-YYYY');
this.totalLicensedNumberOfPages = this.configService.values.LICENSE_PAGE_COUNT || 0;
const startDate = moment(this.configService.values.LICENSE_START, 'DD-MM-YYYY');
const endDate = moment(this.configService.values.LICENSE_END, 'DD-MM-YYYY');
await this._setMonthlyStats(startDate, endDate);
@ -93,7 +91,7 @@ export class LicenseInformationScreenComponent implements OnInit {
}
sendMail(): void {
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
const licenseCustomer = this.configService.values.LICENSE_CUSTOMER;
const subject = this._translateService.instant('license-info-screen.email.title', {
licenseCustomer
});
@ -106,7 +104,7 @@ export class LicenseInformationScreenComponent implements OnInit {
pages: this.totalLicensedNumberOfPages
})
].join(lineBreak);
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
window.location.href = `mailto:${this.configService.values.LICENSE_EMAIL}?subject=${subject}&body=${body}`;
}
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
@ -151,6 +149,7 @@ export class LicenseInformationScreenComponent implements OnInit {
m = startMonth;
y = startYear;
let cumulativePages = 0;
this.barChart = [];
for (const report of reports) {
cumulativePages += report.numberOfAnalyzedPages;
this.barChart.push({

View File

@ -1,12 +1,12 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.content-container,
.right-container {
flex: 1;
padding: 30px;
overflow: auto;
@include scroll-bar;
@include common-mixins.scroll-bar;
}
.right-container {
@ -21,7 +21,7 @@
.template {
padding: 8px 10px;
background-color: $grey-6;
background-color: variables.$grey-6;
border-radius: 4px;
transition: background-color 0.2s;
position: relative;
@ -32,7 +32,7 @@
.name {
max-width: calc(100% - 100px);
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
.actions {
@ -48,7 +48,7 @@
}
&:hover {
background-color: $grey-4;
background-color: variables.$grey-4;
.actions {
display: flex;
@ -81,8 +81,8 @@
grid-template-columns: 1fr 2fr;
.all-caps-label {
border-top: 1px solid $separator;
border-bottom: 1px solid $separator;
border-top: 1px solid variables.$separator;
border-bottom: 1px solid variables.$separator;
padding: 8px 32px;
&:nth-child(2) {
@ -93,7 +93,7 @@
.placeholder,
.description {
padding: 16px 16px;
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
}
.placeholder {

View File

@ -9,7 +9,7 @@ import {
SortingOrders,
TableColumnConfig
} from '@iqser/common-ui';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import * as moment from 'moment';
import { DossiersService } from '../../../dossier/services/dossiers.service';
import { AdminDialogService } from '../../services/admin-dialog.service';
@ -45,14 +45,14 @@ export class TrashScreenComponent extends ListingComponent<DossierListItem> impl
@ViewChild('deletedTimeTemplate', { static: true }) deletedTimeTemplate: TemplateRef<never>;
@ViewChild('restoreDateTemplate', { static: true }) restoreDateTemplate: TemplateRef<never>;
protected readonly _primaryKey = 'dossierName';
private readonly _deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
private readonly _deleteRetentionHours = this._configService.values.DELETE_RETENTION_HOURS;
constructor(
protected readonly _injector: Injector,
private readonly _loadingService: LoadingService,
private readonly _dossiersService: DossiersService,
readonly routerHistoryService: RouterHistoryService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _adminDialogService: AdminDialogService
) {
super(_injector);

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.content-container {
order: 1;
@ -15,7 +15,7 @@
min-width: 353px;
padding: 24px;
border-left: none;
border-right: 1px solid $separator;
border-right: 1px solid variables.$separator;
&.has-scrollbar:hover {
padding-right: 13px;
@ -32,8 +32,8 @@
width: 60px;
height: 60px;
border-radius: 8px;
background-color: $grey-6;
color: $grey-7;
background-color: variables.$grey-6;
color: variables.$grey-7;
align-items: center;
justify-content: center;
text-align: center;
@ -46,7 +46,7 @@
}
&:not(.disabled):not(.active):hover {
background-color: darken($grey-6, 2);
background-color: darken(variables.$grey-6, 2);
}
&:not(:last-child) {
@ -54,8 +54,8 @@
}
&.active {
background-color: $primary;
color: $white;
background-color: variables.$primary;
color: variables.$white;
}
&.times-new-roman {

View File

@ -5,9 +5,8 @@ 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, IconButtonTypes, LoadingService } from '@iqser/common-ui';
import { Debounce, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
import { WatermarkControllerService, WatermarkModelRes } from '@redaction/red-ui-http';
import { Toaster } from '@iqser/common-ui';
import { ActivatedRoute } from '@angular/router';
import { BASE_HREF } from '../../../../tokens';
import { stampPDFPage } from '@utils/page-stamper';
@ -137,7 +136,7 @@ export class WatermarkScreenComponent implements OnInit {
).then(instance => {
this._instance = instance;
instance.docViewer.on('documentLoaded', async () => {
instance.Core.documentViewer.on('documentLoaded', async () => {
this._loadingService.stop();
await this._drawWatermark();
});
@ -149,19 +148,19 @@ export class WatermarkScreenComponent implements OnInit {
responseType: 'blob'
})
.subscribe(blobData => {
this._instance.loadDocument(blobData, { filename: 'blank.pdf' });
this._instance.UI.loadDocument(blobData, { filename: 'blank.pdf' });
});
});
}
}
private _disableElements() {
this._instance.disableElements(['header', 'toolsHeader', 'pageNavOverlay', 'textPopup']);
this._instance.UI.disableElements(['header', 'toolsHeader', 'pageNavOverlay', 'textPopup']);
}
private async _drawWatermark() {
const pdfNet = this._instance.PDFNet;
const document = await this._instance.docViewer.getDocument().getPDFDoc();
const pdfNet = this._instance.Core.PDFNet;
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
const text = this.configForm.get('text').value || '';
const fontSize = this.configForm.get('fontSize').value;
@ -171,8 +170,8 @@ export class WatermarkScreenComponent implements OnInit {
const color = this.configForm.get('hexColor').value;
await stampPDFPage(document, pdfNet, text, fontSize, fontType, orientation, opacity, color, [1]);
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([0], 0);
this._instance.Core.documentViewer.refreshAll();
this._instance.Core.documentViewer.updateView([0], 0);
this._changeDetectorRef.detectChanges();
}

View File

@ -1,11 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { AppConfigService } from './app-config.service';
@NgModule({
declarations: [],
imports: [CommonModule, HttpClientModule],
providers: [AppConfigService]
})
export class AppConfigModule {}

View File

@ -1,83 +0,0 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { Title } from '@angular/platform-browser';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import packageInfo from '../../../../../../package.json';
import { CacheApiService, wipeCaches } from '@redaction/red-cache';
export enum AppConfigKey {
OAUTH_URL = 'OAUTH_URL',
OAUTH_CLIENT_ID = 'OAUTH_CLIENT_ID',
OAUTH_IDP_HINT = 'OAUTH_IDP_HINT',
API_URL = 'API_URL',
ADMIN_CONTACT_NAME = 'ADMIN_CONTACT_NAME',
ADMIN_CONTACT_URL = 'ADMIN_CONTACT_URL',
AUTO_READ_TIME = 'AUTO_READ_TIME',
SELECTION_MODE = 'SELECTION_MODE',
MAX_FILE_SIZE_MB = 'MAX_FILE_SIZE_MB',
RECENT_PERIOD_IN_HOURS = 'RECENT_PERIOD_IN_HOURS',
DELETE_RETENTION_HOURS = 'DELETE_RETENTION_HOURS',
APP_NAME = 'APP_NAME',
// TODO
BACKEND_APP_VERSION = 'BACKEND_APP_VERSION',
FRONTEND_APP_VERSION = 'FRONTEND_APP_VERSION',
EULA_URL = 'EULA_URL',
LICENSE_CUSTOMER = 'LICENSE_CUSTOMER',
LICENSE_EMAIL = 'LICENSE_EMAIL',
LICENSE_START = 'LICENSE_START',
LICENSE_END = 'LICENSE_END',
LICENSE_PAGE_COUNT = 'LICENSE_PAGE_COUNT'
}
@Injectable({
providedIn: 'root'
})
export class AppConfigService {
private _config: { [key in AppConfigKey]?: any } = {};
constructor(
private readonly _httpClient: HttpClient,
private readonly _cacheApiService: CacheApiService,
private readonly _titleService: Title
) {}
get version(): string {
return packageInfo.version;
}
loadAppConfig(): Observable<any> {
this._cacheApiService.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION).then(async lastVersion => {
console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', this.version);
if (lastVersion !== this.version) {
console.log('[REDACTION] Version-missmatch - wiping caches!');
await wipeCaches();
}
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, this.version);
});
return this._httpClient.get<any>('/assets/config/config.json').pipe(
tap(config => {
console.log('[REDACTION] Started with config: ', config);
this._config = config;
this._config[AppConfigKey.FRONTEND_APP_VERSION] = this.version;
})
);
}
updateDisplayName(name: string) {
this.setConfig(AppConfigKey.APP_NAME, name);
this._titleService.setTitle(this.getConfig(AppConfigKey.APP_NAME, 'RedactManager'));
}
setConfig(key: AppConfigKey, value: any) {
this._config[key] = value;
}
getConfig(key: AppConfigKey | string, defaultValue?: any) {
return this._config[key] ? this._config[key] : defaultValue;
}
}

View File

@ -1,28 +0,0 @@
import { GeneralSettingsControllerService } from '@redaction/red-ui-http';
import { catchError, mergeMap, take, tap } from 'rxjs/operators';
import { AppConfigService } from './app-config.service';
import { Title } from '@angular/platform-browser';
import { APP_BOOTSTRAPPED } from '../bootstrap/app-bootstrap';
import { of } from 'rxjs';
export function configurationInitializer(
title: Title,
appConfigService: AppConfigService,
generalSettingsControllerService: GeneralSettingsControllerService
) {
return () =>
APP_BOOTSTRAPPED.pipe(
mergeMap(() =>
generalSettingsControllerService.getGeneralConfigurations().pipe(
tap(configuration => {
appConfigService.updateDisplayName(configuration.displayName);
})
)
),
catchError(() => {
title.setTitle('RedactManager');
return of({});
}),
take(1)
).toPromise();
}

View File

@ -2,7 +2,7 @@ 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 { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { BASE_HREF } from '../../tokens';
@Injectable({
@ -10,19 +10,20 @@ import { BASE_HREF } from '../../tokens';
})
export class AuthGuard extends KeycloakAuthGuard {
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
protected readonly _router: Router,
private readonly _userService: UserService,
protected readonly _keycloak: KeycloakService,
private readonly _appConfigService: AppConfigService,
private readonly _userService: UserService
private readonly _configService: ConfigService,
@Inject(BASE_HREF) private readonly _baseHref: string
) {
super(_router, _keycloak);
}
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
if (!this.authenticated) {
const kcIdpHint = route.queryParamMap.get('kc_idp_hint');
await this._keycloak.login({
idpHint: this._appConfigService.getConfig(AppConfigKey.OAUTH_IDP_HINT, null),
idpHint: kcIdpHint ?? this._configService.values.OAUTH_IDP_HINT,
redirectUri: window.location.origin + this._baseHref + state.url
});
return false;

View File

@ -1,59 +1,50 @@
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 { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { BASE_HREF } from '../../tokens';
import { APP_BOOTSTRAPPED } from '../bootstrap/app-bootstrap';
import { environment } from '@environments/environment';
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) {
return () =>
appConfigService
.loadAppConfig()
.toPromise()
.then(() => {
let url = appConfigService.getConfig(AppConfigKey.OAUTH_URL);
url = url.replace(/\/$/, ''); // remove trailing slash
const realm = url.substring(url.lastIndexOf('/') + 1, url.length);
url = url.substr(0, url.lastIndexOf('/realms'));
const options: KeycloakOptions = {
config: {
url: url,
realm: realm,
clientId: appConfigService.getConfig(AppConfigKey.OAUTH_CLIENT_ID)
},
initOptions: {
checkLoginIframe: false,
onLoad: 'check-sso',
silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
flow: 'standard'
},
enableBearerInterceptor: true
};
return keycloak
.init(options)
.then(() => configureAutomaticRedirectToLoginScreen(keycloak))
.then(() => APP_BOOTSTRAPPED.next(true));
});
export function keycloakInitializer(keycloakService: KeycloakService, configService: ConfigService, baseUrl: string): () => Promise<void> {
let url = configService.values.OAUTH_URL;
url = url.replace(/\/$/, ''); // remove trailing slash
const realm = url.substring(url.lastIndexOf('/') + 1, url.length);
url = url.substr(0, url.lastIndexOf('/realms'));
const options: KeycloakOptions = {
config: {
url: url,
realm: realm,
clientId: configService.values.OAUTH_CLIENT_ID
},
initOptions: {
checkLoginIframe: false,
onLoad: 'login-required',
silentCheckSsoRedirectUri: environment.production
? window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html'
: null,
flow: 'standard'
},
enableBearerInterceptor: true
};
return () => keycloakService.init(options).then(() => configureAutomaticRedirectToLoginScreen(keycloakService));
}
function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakService) {
keyCloakService.getKeycloakInstance().onAuthRefreshError = () => {
keyCloakService.logout();
keyCloakService.getKeycloakInstance().onAuthRefreshError = async () => {
await keyCloakService.logout();
};
}
@NgModule({
declarations: [],
imports: [CommonModule, HttpClientModule, KeycloakAngularModule, AppConfigModule],
imports: [CommonModule, HttpClientModule, KeycloakAngularModule],
providers: [
{
provide: APP_INITIALIZER,
useFactory: keycloakInitializer,
multi: true,
deps: [KeycloakService, AppConfigService, BASE_HREF]
deps: [KeycloakService, ConfigService, BASE_HREF]
}
]
})

View File

@ -1,3 +0,0 @@
import { ReplaySubject } from 'rxjs';
export const APP_BOOTSTRAPPED = new ReplaySubject<boolean>();

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.annotation-actions {
display: none;
@ -8,7 +8,7 @@
}
.confirm.active {
background-color: $grey-2;
background-color: variables.$grey-2;
}
> *:not(:last-child) {

View File

@ -48,8 +48,8 @@ export class AnnotationActionsComponent implements OnInit {
}
get viewerAnnotations() {
if (this.viewer?.annotManager) {
return this._annotations.map(a => this.viewer?.annotManager?.getAnnotationById(a.id));
if (this.viewer?.Core.annotationManager) {
return this._annotations.map(a => this.viewer?.Core.annotationManager?.getAnnotationById(a.id));
} else {
return [];
}
@ -78,15 +78,15 @@ export class AnnotationActionsComponent implements OnInit {
hideAnnotation($event: MouseEvent) {
$event.stopPropagation();
this.viewer.annotManager.hideAnnotations(this.viewerAnnotations);
this.viewer.annotManager.deselectAllAnnotations();
this.viewer.Core.annotationManager.hideAnnotations(this.viewerAnnotations);
this.viewer.Core.annotationManager.deselectAllAnnotations();
this.annotationActionsService.updateHiddenAnnotation(this.annotations, this.viewerAnnotations, true);
}
showAnnotation($event: MouseEvent) {
$event.stopPropagation();
this.viewer.annotManager.showAnnotations(this.viewerAnnotations);
this.viewer.annotManager.deselectAllAnnotations();
this.viewer.Core.annotationManager.showAnnotations(this.viewerAnnotations);
this.viewer.Core.annotationManager.deselectAllAnnotations();
this.annotationActionsService.updateHiddenAnnotation(this.annotations, this.viewerAnnotations, false);
}

View File

@ -177,7 +177,22 @@ export class DossierOverviewBulkActionsComponent {
}
approveDocuments() {
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
const foundUpdatedFile = this.selectedFiles.find(file => file.hasUpdates);
if (foundUpdatedFile) {
this._dialogService.openDialog(
'confirm',
null,
new ConfirmationDialogInput({
title: _('confirmation-dialog.approve-multiple-files.title'),
question: _('confirmation-dialog.approve-multiple-files.question')
}),
() => {
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
}
);
} else {
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
}
}
assignToMe() {

View File

@ -1,9 +1,10 @@
<div *ngFor="let comment of annotation.comments" class="comment">
<div class="comment-details-wrapper">
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label">
<b> {{ getOwnerName(comment) }} </b>
<strong> {{ comment.user | name }} </strong>
{{ comment.date | date: 'sophisticatedDate' }}
</div>
<div class="comment-actions">
<iqser-circle-button
(action)="deleteComment(comment)"
@ -15,6 +16,7 @@
></iqser-circle-button>
</div>
</div>
<div>{{ comment.text }}</div>
</div>

View File

@ -1,5 +1,3 @@
@import '../../../../../assets/styles/variables';
:host {
display: flex;
flex-direction: column;

View File

@ -52,8 +52,4 @@ export class CommentsComponent {
}
});
}
getOwnerName(comment: Comment): string {
return this._userService.getNameForId(comment.user);
}
}

View File

@ -1,5 +1,5 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
:host {
display: block;
@ -13,7 +13,7 @@
.right-content {
flex-direction: column;
@include scroll-bar;
@include common-mixins.scroll-bar;
overflow: hidden;
&:hover {
@ -42,7 +42,7 @@
}
&:not(:last-child) {
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
}
}

View File

@ -1,6 +1,6 @@
@import '../../../../../assets/styles/variables';
@import '../../../../../assets/styles/red-components';
@import 'libs/common-ui/src/assets/styles/texts';
@use 'variables';
@use 'red-components';
@use 'common-texts';
:host {
@extend .stats-subtitle;
@ -22,7 +22,7 @@
transition: background-color 0.2s;
&:hover {
background-color: $grey-6;
background-color: variables.$grey-6;
}
}
}

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.header-wrapper {
display: flex;
@ -29,11 +29,11 @@
padding: 3px 8px;
&:hover {
background-color: $grey-6;
background-color: variables.$grey-6;
}
&.active {
background-color: rgba($primary, 0.1);
background-color: rgba(variables.$primary, 0.1);
}
&:not(:last-of-type) {

View File

@ -1,5 +1,3 @@
@import '../../../../../assets/styles/variables';
:host {
flex: 1;
display: flex;

View File

@ -7,7 +7,7 @@
<ng-container *ngTemplateOutlet="actions"></ng-container>
</ng-container>
<ng-template #actions>
<ng-template #actions redactionLongPress (longPress)="forceReanalysisAction($event)">
<div *ngIf="fileStatus" class="file-actions">
<iqser-circle-button
(action)="openDocument()"
@ -145,7 +145,6 @@
[type]="circleButtonTypes.dark"
icon="iqser:refresh"
></iqser-circle-button>
<!-- exclude from redaction -->
<div class="iqser-input-group">
<mat-slide-toggle

View File

@ -1,9 +1,9 @@
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'common-mixins';
.file-actions {
display: flex;
overflow-y: auto;
@include no-scroll-bar;
@include common-mixins.no-scroll-bar;
> *:not(:last-child) {
margin-right: 2px;

View File

@ -10,6 +10,8 @@ import { FileManagementControllerService, FileStatus } from '@redaction/red-ui-h
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@services/user.service';
import { filter } from 'rxjs/operators';
import { UserPreferenceService } from '../../../../services/user-preference.service';
import { LongPressEvent } from '../../../shared/directives/long-press.directive';
@Component({
selector: 'redaction-file-actions',
@ -55,7 +57,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
private readonly _loadingService: LoadingService,
private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _userService: UserService,
private readonly _toaster: Toaster
private readonly _toaster: Toaster,
private readonly _userPreferenceService: UserPreferenceService
) {
super();
}
@ -152,8 +155,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
});
}
reanalyseFile($event: MouseEvent) {
$event.stopPropagation();
reanalyseFile($event?: MouseEvent) {
if ($event) {
$event.stopPropagation();
}
this.addSubscription = this._fileActionService.reanalyseFile(this.fileStatus).subscribe(() => {
this.reloadDossiers('reanalyse');
});
@ -172,9 +177,21 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
setFileApproved($event: MouseEvent) {
$event.stopPropagation();
this.addSubscription = this._fileActionService.setFileApproved(this.fileStatus).subscribe(() => {
this.reloadDossiers('set-approved');
});
if (this.fileStatus.hasUpdates) {
this._dialogService.openDialog(
'confirm',
$event,
new ConfirmationDialogInput({
title: _('confirmation-dialog.approve-file.title'),
question: _('confirmation-dialog.approve-file.question')
}),
() => {
this._setFileApproved();
}
);
} else {
this._setFileApproved();
}
}
ocrFile($event: MouseEvent) {
@ -212,6 +229,18 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnInit, OnD
}
}
forceReanalysisAction($event: LongPressEvent) {
if (this._userPreferenceService.areDevFeaturesEnabled) {
this.canReanalyse = $event.touchEnd ? this.permissionsService.canReanalyseFile(this.fileStatus) : true;
}
}
private _setFileApproved() {
this.addSubscription = this._fileActionService.setFileApproved(this.fileStatus).subscribe(() => {
this.reloadDossiers('set-approved');
});
}
private _setup() {
this.statusBarConfig = [{ color: this.fileStatus.status, length: 1 }];
this.tooltipPosition = this.isFilePreview ? 'below' : 'above';

View File

@ -0,0 +1,23 @@
<ng-container *ngIf="hasEnginesToShow">
<div cdkOverlayOrigin #trigger="cdkOverlayOrigin" class="chip" (mouseover)="isPopoverOpen = true" (mouseout)="isPopoverOpen = false">
<ng-container *ngFor="let engine of engines">
<mat-icon *ngIf="engine.show" [svgIcon]="engine.icon"></mat-icon>
</ng-container>
</div>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOffsetY]="-8"
[cdkConnectedOverlayOrigin]="trigger"
[cdkConnectedOverlayOpen]="isPopoverOpen"
>
<div class="popover">
<ng-container *ngFor="let engine of engines">
<div *ngIf="engine.show" class="flex-align-items-center">
<mat-icon [svgIcon]="engine.icon"></mat-icon>
<span>{{ engine.description }}</span>
</div>
</ng-container>
</div>
</ng-template>
</ng-container>

View File

@ -0,0 +1,51 @@
@use 'variables';
.popover {
width: 260px;
padding: 10px;
border-radius: 3px;
background-color: variables.$grey-1;
color: variables.$white;
mat-icon {
color: variables.$white;
flex-shrink: 0;
}
span {
padding-left: 8px;
font-size: 11px;
line-height: 14px;
}
}
.chip {
height: 24px;
&:hover {
background-color: variables.$grey-6;
border-radius: 12px;
mat-icon {
opacity: 1;
}
}
mat-icon {
opacity: 50%;
margin-left: 3px;
margin-right: 3px;
&:first-of-type {
margin-left: 8px;
}
&:last-of-type {
margin-right: 8px;
}
}
}
mat-icon {
width: 10px;
}

View File

@ -0,0 +1,65 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { OnChange } from '@iqser/common-ui';
import { TranslateService } from '@ngx-translate/core';
interface Engine {
readonly icon: string;
readonly description: string;
readonly show: boolean;
}
type Engines = readonly Engine[];
const Engines = {
DICTIONARY: 'DICTIONARY',
NER: 'NER',
RULE: 'RULE'
} as const;
type EngineName = keyof typeof Engines;
@Component({
selector: 'redaction-annotation-source',
templateUrl: './annotation-source.component.html',
styleUrls: ['./annotation-source.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AnnotationSourceComponent {
@Input()
@OnChange<AnnotationWrapper, AnnotationSourceComponent>('updateEngines')
annotation: AnnotationWrapper;
isPopoverOpen = false;
engines: Engines;
constructor(private readonly _translateService: TranslateService) {}
get hasEnginesToShow(): boolean {
return this.engines.length && this.engines.some(source => source.show);
}
updateEngines(): void {
this.engines = [
{
icon: 'red:dictionary',
description: this._translateService.instant('annotation-engines.dictionary'),
show: this._isBasedOn(Engines.DICTIONARY)
},
{
icon: 'red:ai',
description: this._translateService.instant('annotation-engines.ner'),
show: this._isBasedOn(Engines.NER)
},
{
icon: 'red:rule',
description: this._translateService.instant('annotation-engines.rule', { rule: this.annotation.legalBasisValue }),
show: this._isBasedOn(Engines.RULE)
}
];
}
private _isBasedOn(engineName: EngineName) {
return !!this.annotation.engines?.includes(engineName);
}
}

View File

@ -0,0 +1,62 @@
<div
(click)="annotationClicked(annotation, $event)"
*ngFor="let annotation of annotations"
[attr.annotation-id]="annotation.id"
[attr.annotation-page]="activeViewerPage"
[class.active]="isSelected(annotation.annotationId)"
[class.multi-select-active]="multiSelectActive"
class="annotation-wrapper"
>
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
<redaction-type-annotation-icon [annotation]="annotation | log"></redaction-type-annotation-icon>
<div class="flex-1">
<div>
<strong>{{ annotation.typeLabel | translate }}</strong>
</div>
<div *ngIf="annotation?.type !== 'manual'">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.type | humanize: false }}
</div>
<div *ngIf="annotation.shortContent && !annotation.isHint">
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
</div>
</div>
<div class="active-icon-marker-container">
<iqser-round-checkbox
*ngIf="multiSelectActive && isSelected(annotation.annotationId)"
[active]="true"
></iqser-round-checkbox>
</div>
</div>
<div class="actions-wrapper">
<div
(click)="comments.toggleExpandComments($event)"
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
class="comments-counter"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.comments.length }}
</div>
<div *ngIf="!multiSelectActive" class="actions">
<ng-container
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
></ng-container>
</div>
</div>
<redaction-comments #comments [annotation]="annotation"></redaction-comments>
</div>
<redaction-annotation-source [annotation]="annotation"></redaction-annotation-source>
</div>

View File

@ -0,0 +1,96 @@
@use 'variables';
:host {
width: 100%;
position: relative;
}
.annotation-wrapper {
display: flex;
width: 100%;
border-bottom: 1px solid variables.$separator;
.active-bar-marker {
min-width: 4px;
min-height: 100%;
}
.active-icon-marker-container {
min-width: 20px;
}
&.active {
&:not(.lower-height) .active-bar-marker {
background-color: variables.$primary;
}
}
.annotation {
padding: 10px 16px 8px 10px;
font-size: 11px;
line-height: 14px;
cursor: pointer;
display: flex;
flex-direction: column;
width: 100%;
&.removed {
text-decoration: line-through;
color: variables.$grey-7;
}
.details {
display: flex;
position: relative;
}
.actions-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
min-height: 34px;
padding-left: 18px;
.comments-counter {
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
transition: background-color 0.2s;
line-height: 13px;
height: 24px;
border-radius: 12px;
mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
}
&:hover {
background-color: variables.$grey-4;
}
}
}
redaction-type-annotation-icon {
margin-top: 6px;
margin-right: 10px;
}
}
&:hover {
background-color: variables.$grey-8;
::ng-deep .annotation-actions {
display: flex;
}
}
}
redaction-annotation-source {
position: absolute;
top: 6px;
right: 8px;
}

View File

@ -0,0 +1,48 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { IqserEventTarget } from '@iqser/common-ui';
@Component({
selector: 'redaction-annotations-list',
templateUrl: './annotations-list.component.html',
styleUrls: ['./annotations-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AnnotationsListComponent {
@Input() annotations: AnnotationWrapper[];
@Input() selectedAnnotations: AnnotationWrapper[];
@Input() annotationActionsTemplate: TemplateRef<unknown>;
@Input() multiSelectActive = false;
@Input() activeViewerPage: number;
@Input() canMultiSelect = true;
@Output() readonly multiSelectActiveChange = new EventEmitter<boolean>();
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
@Output() readonly selectAnnotations = new EventEmitter<
AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
>();
@Output() readonly deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
if (($event.target as IqserEventTarget).localName === 'input') {
return;
}
this.pagesPanelActive.emit(false);
if (this.isSelected(annotation.annotationId)) {
this.deselectAnnotations.emit([annotation]);
} else {
if (this.canMultiSelect && ($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
this.multiSelectActive = true;
this.multiSelectActiveChange.emit(true);
}
this.selectAnnotations.emit({
annotations: [annotation],
multiSelect: this.multiSelectActive
});
}
}
isSelected(annotationId: string): boolean {
return !!this.selectedAnnotations?.find(a => a?.annotationId === annotationId);
}
}

View File

@ -1,6 +1,3 @@
<!-- This is a hack to subscribe to an observable using async pipe instead of component class -->
<ng-container *ngIf="displayedAnnotations$ | async"></ng-container>
<div *ngIf="!excludePages" class="right-title heading" translate="file-preview.tabs.annotations.label">
<div>
<div
@ -150,8 +147,7 @@
(click)="actionPerformed.emit('view-exclude-pages')"
class="with-underline"
translate="file-preview.excluded-from-redaction"
>
</a
></a
>.
</ng-container>
</iqser-empty-state>
@ -174,62 +170,17 @@
</div>
</ng-container>
<div
(click)="annotationClicked(annotation, $event)"
*ngFor="let annotation of displayedAnnotations.get(activeViewerPage)"
[attr.annotation-id]="annotation.id"
[attr.annotation-page]="activeViewerPage"
[class.active]="isSelected(annotation)"
[class.multi-select-active]="multiSelectActive"
class="annotation-wrapper"
>
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
<div class="flex-1">
<div>
<strong>{{ annotation.typeLabel | translate }}</strong>
</div>
<div *ngIf="annotation?.type !== 'manual'">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>: </strong
>{{ annotation.type | humanize: false }}
</div>
<div *ngIf="annotation.shortContent && !annotation.isHint">
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
</div>
</div>
<div class="active-icon-marker-container">
<iqser-round-checkbox
*ngIf="multiSelectActive && isSelected(annotation)"
[active]="true"
></iqser-round-checkbox>
</div>
</div>
<div class="actions-wrapper">
<div
(click)="toggleExpandComments(annotation, $event)"
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
class="comments-counter"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.comments.length }}
</div>
<div *ngIf="!multiSelectActive" class="actions">
<ng-container
[ngTemplateOutletContext]="{ annotation: annotation }"
[ngTemplateOutlet]="annotationActionsTemplate"
></ng-container>
</div>
</div>
<redaction-comments [annotation]="annotation"></redaction-comments>
</div>
</div>
<redaction-annotations-list
[canMultiSelect]="!isReadOnly"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
[selectedAnnotations]="selectedAnnotations"
[annotationActionsTemplate]="annotationActionsTemplate"
[(multiSelectActive)]="multiSelectActive"
[activeViewerPage]="activeViewerPage"
(pagesPanelActive)="pagesPanelActive = $event"
(selectAnnotations)="selectAnnotations.emit($event)"
(deselectAnnotations)="deselectAnnotations.emit($event)"
></redaction-annotations-list>
</div>
</ng-container>

View File

@ -1,10 +1,10 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.read-only {
padding: 13px 16px;
background-color: $primary;
color: $white;
background-color: variables.$primary;
color: variables.$white;
justify-content: space-between;
.read-only-text {
@ -48,12 +48,12 @@
.multi-select {
min-height: 40px;
background: $primary;
background: variables.$primary;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 8px 0 16px;
color: $white;
color: variables.$white;
.selected-wrapper {
display: flex;
@ -90,7 +90,7 @@
}
.quick-navigation {
border-right: 1px solid $separator;
border-right: 1px solid variables.$separator;
min-width: 61px;
overflow: hidden;
display: flex;
@ -105,7 +105,7 @@
transition: background-color 0.25s;
&:not(.disabled):hover {
background-color: $grey-6;
background-color: variables.$grey-6;
}
mat-icon {
@ -124,21 +124,21 @@
}
.pages {
@include no-scroll-bar;
@include common-mixins.no-scroll-bar;
overflow: auto;
flex: 1;
}
}
.page-separator {
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
height: 32px;
box-sizing: border-box;
padding: 0 10px;
display: flex;
align-items: center;
justify-content: space-between;
background-color: $grey-6;
background-color: variables.$grey-6;
> div {
display: flex;
@ -157,96 +157,12 @@
align-items: center;
flex-direction: column;
.annotation-wrapper {
display: flex;
width: 100%;
border-bottom: 1px solid $separator;
.active-bar-marker {
min-width: 4px;
min-height: 100%;
}
.active-icon-marker-container {
min-width: 20px;
}
&.active {
&:not(.lower-height) .active-bar-marker {
background-color: $primary;
}
}
.annotation {
padding: 10px 16px 8px 10px;
font-size: 11px;
line-height: 14px;
cursor: pointer;
display: flex;
flex-direction: column;
width: 100%;
&.removed {
text-decoration: line-through;
color: $grey-7;
}
.details {
display: flex;
position: relative;
}
.actions-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
min-height: 34px;
padding-left: 18px;
.comments-counter {
cursor: pointer;
display: flex;
align-items: center;
padding: 0 8px;
transition: background-color 0.2s;
line-height: 13px;
height: 24px;
border-radius: 12px;
mat-icon {
width: 10px;
height: 10px;
margin-right: 4px;
}
&:hover {
background-color: $grey-4;
}
}
}
redaction-type-annotation-icon {
margin-top: 6px;
margin-right: 10px;
}
}
&:hover {
background-color: $grey-8;
::ng-deep .annotation-actions {
display: flex;
}
}
}
&:hover {
overflow-y: auto;
@include scroll-bar;
@include common-mixins.scroll-bar;
}
&.has-scrollbar:hover .annotation-wrapper .annotation {
&.has-scrollbar:hover::ng-deep .annotation-wrapper .annotation {
padding-right: 5px;
}
}

View File

@ -1,23 +1,10 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
HostListener,
Input,
Output,
QueryList,
TemplateRef,
ViewChild,
ViewChildren
} from '@angular/core';
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';
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 { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, IqserEventTarget, NestedFilter } from '@iqser/common-ui';
import { FileDataModel } from '@models/file/file-data.model';
import { CommentsComponent } from '../comments/comments.component';
import { PermissionsService } from '@services/permissions.service';
import { WebViewerInstance } from '@pdftron/webviewer';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
@ -56,7 +43,6 @@ export class FileWorkloadComponent {
@Output() readonly actionPerformed = new EventEmitter<string>();
displayedPages: number[] = [];
pagesPanelActive = true;
@ViewChildren(CommentsComponent) readonly annotationCommentsComponents: QueryList<CommentsComponent>;
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
@ -118,15 +104,6 @@ export class FileWorkloadComponent {
}
}
isSelected(annotation: AnnotationWrapper) {
return this.selectedAnnotations?.find(a => a?.id === annotation.id);
}
toggleExpandComments(annotation: AnnotationWrapper, $event: MouseEvent) {
$event.stopPropagation();
this.annotationCommentsComponents.find(c => c.annotation === annotation).toggleExpandComments();
}
logAnnotation(annotation: AnnotationWrapper) {
console.log(annotation);
}
@ -156,25 +133,6 @@ export class FileWorkloadComponent {
return this.displayedAnnotations;
}
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
if (($event.target as IqserEventTarget).localName === 'input') {
return;
}
this.pagesPanelActive = false;
this.logAnnotation(annotation);
if (this.isSelected(annotation)) {
this.deselectAnnotations.emit([annotation]);
} else {
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
this.multiSelectActive = true;
}
this.selectAnnotations.emit({
annotations: [annotation],
multiSelect: this.multiSelectActive
});
}
}
@HostListener('window:keyup', ['$event'])
handleKeyEvent($event: KeyboardEvent): void {
if (

View File

@ -1,5 +1,3 @@
@import '../../../../../assets/styles/variables';
.needs-work {
display: flex;
flex-direction: row;

View File

@ -1,5 +1,5 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
:host {
height: 100%;
@ -7,13 +7,13 @@
flex-direction: column;
.exclude-pages-input-container {
background-color: $grey-6;
background-color: variables.$grey-6;
padding: 15px 15px 16px 14px;
}
.all-caps-label-container {
padding: 8px 16px;
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
}
.ranges {
@ -22,7 +22,7 @@
.range {
padding-left: 17px;
padding-right: 16px;
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
transition: background-color 0.2s;
display: flex;
align-items: center;
@ -34,7 +34,7 @@
}
&:hover {
background-color: $grey-8;
background-color: variables.$grey-8;
iqser-circle-button {
display: initial;
@ -43,7 +43,7 @@
}
&.has-scrollbar:hover {
@include scroll-bar;
@include common-mixins.scroll-bar;
overflow: auto;
.range {

View File

@ -1,18 +1,18 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.page-wrapper {
color: $accent;
color: variables.$accent;
position: relative;
padding: 12px 14px 12px 8px;
cursor: pointer;
border-left: 4px solid transparent;
&:hover {
background-color: $grey-2;
background-color: variables.$grey-2;
}
&.active {
border-left: 4px solid $primary;
border-left: 4px solid variables.$primary;
}
mat-icon {
@ -35,15 +35,15 @@
}
&.read {
color: $grey-5;
color: variables.$grey-5;
.text {
color: $accent;
color: variables.$accent;
}
}
.dot {
background: $primary;
background: variables.$primary;
height: 8px;
width: 8px;
border-radius: 50%;

View File

@ -2,7 +2,7 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, S
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 { ConfigService } from '@services/config.service';
import { Subscription } from 'rxjs';
@Component({
@ -25,7 +25,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
constructor(
private readonly _viewedPagesControllerService: ViewedPagesControllerService,
private readonly _appStateService: AppStateService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _permissionService: PermissionsService
) {}
@ -67,11 +67,11 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
clearTimeout(this.pageReadTimeout);
}
if (this.active && !this.read) {
this.pageReadTimeout = setTimeout(() => {
this.pageReadTimeout = window.setTimeout(() => {
if (this.active && !this.read) {
this._markPageRead();
}
}, this._appConfigService.getConfig(AppConfigKey.AUTO_READ_TIME, 1.5) * 1000);
}, this._configService.values.AUTO_READ_TIME * 1000);
}
}
}

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.page {
display: flex;
@ -28,9 +28,9 @@
bottom: 20px;
left: 50%;
transform: translate(-50%);
background: $white;
color: $grey-7;
border: 1px solid $grey-7;
background: variables.$white;
color: variables.$grey-7;
border: 1px solid variables.$grey-7;
border-radius: 8px;
padding: 6px 2px;
display: flex;
@ -56,7 +56,7 @@
margin: 0;
}
color: $grey-7;
color: variables.$grey-7;
text-decoration: none;
outline: none;
border: none;
@ -67,7 +67,7 @@
&:hover,
&:focus {
font-weight: bold;
border-bottom: 1px solid $grey-7;
border-bottom: 1px solid variables.$grey-7;
}
}
@ -79,7 +79,7 @@
padding-right: 4px;
&:hover {
color: $accent;
color: variables.$accent;
}
}
}

View File

@ -12,7 +12,7 @@ import {
ViewChild
} from '@angular/core';
import { ManualRedactionEntry } from '@redaction/red-ui-http';
import WebViewer, { Annotations, Tools, WebViewerInstance } from '@pdftron/webviewer';
import WebViewer, { Core, 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';
@ -23,7 +23,7 @@ import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationActionsService } from '../../services/annotation-actions.service';
import { UserPreferenceService } from '@services/user-preference.service';
import { BASE_HREF } from '../../../../tokens';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { LoadingService } from '@iqser/common-ui';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { ConfirmationDialogInput } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
@ -33,6 +33,8 @@ import { ViewMode } from '@models/file/view-mode';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ActivatedRoute } from '@angular/router';
import TextTool = Tools.TextTool;
import Tools = Core.Tools;
import Annotation = Core.Annotations.Annotation;
@Component({
selector: 'redaction-pdf-viewer',
@ -71,7 +73,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
private readonly _userPreferenceService: UserPreferenceService,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _annotationActionsService: AnnotationActionsService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _loadingService: LoadingService
) {}
@ -107,11 +109,11 @@ export class PdfViewerComponent implements OnInit, OnChanges {
setInitialViewerState() {
// viewer init
this.instance.setFitMode('FitPage');
this.instance.UI.setFitMode('FitPage');
const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode();
const instanceDisplayMode = this.instance.Core.documentViewer.getDisplayModeManager().getDisplayMode();
instanceDisplayMode.mode = this.viewMode === 'STANDARD' ? 'Single' : 'Facing';
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
this.instance.Core.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
}
async uploadFile(files: any) {
@ -123,13 +125,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
if (fileToCompare) {
fileReader.onload = async () => {
const pdfData = fileReader.result;
const pdfNet = this.instance.Core.PDFNet;
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const mergedDocument = await PDFNet.PDFDoc.create();
const compareDocument = await PDFNet.PDFDoc.createFromBuffer(<any>pdfData);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
const mergedDocument = await pdfNet.PDFDoc.create();
const compareDocument = await pdfNet.PDFDoc.createFromBuffer(<any>pdfData);
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
const currentDocumentPageCount = await currentDocument.getPageCount();
const compareDocumentPageCount = await compareDocument.getPageCount();
@ -151,7 +153,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
() => {
this.utils.navigateToPage(1);
},
PDFNet
this.instance.Core.PDFNet
);
this._loadingService.stop();
};
@ -182,14 +184,14 @@ export class PdfViewerComponent implements OnInit, OnChanges {
async closeCompareMode() {
this.viewMode = 'STANDARD';
const PDFNet = this.instance.PDFNet;
await PDFNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const currentDocument = await PDFNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
this.instance.loadDocument(currentDocument, {
const pdfNet = this.instance.Core.PDFNet;
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.fileData.arrayBuffer());
this.instance.UI.loadDocument(currentDocument, {
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
});
this.instance.disableElements(['closeCompareButton']);
this.instance.enableElements(['compareButton']);
this.instance.UI.disableElements(['closeCompareButton']);
this.instance.UI.enableElements(['compareButton']);
this.utils.navigateToPage(1);
}
@ -216,8 +218,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
this.utils.disableHotkeys();
this._configureTextPopup();
this.instance.annotManager.on('annotationSelected', (annotations, action) => {
this.annotationSelected.emit(this.instance.annotManager.getSelectedAnnotations().map(ann => ann.Id));
this.instance.Core.annotationManager.on('annotationSelected', (annotations, action) => {
this.annotationSelected.emit(this.instance.Core.annotationManager.getSelectedAnnotations().map(ann => ann.Id));
if (action === 'deselected') {
this._toggleRectangleAnnotationAction(true);
} else {
@ -226,16 +228,16 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
});
this.instance.annotManager.on('annotationChanged', annotations => {
this.instance.Core.annotationManager.on('annotationChanged', annotations => {
// when a rectangle is drawn,
// it returns one annotation with tool name 'AnnotationCreateRectangle;
// this will auto select rectangle after drawing
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
this.instance.annotManager.selectAnnotations(annotations);
this.instance.Core.annotationManager.selectAnnotations(annotations);
}
});
this.instance.docViewer.on('pageNumberUpdated', pageNumber => {
this.instance.Core.documentViewer.on('pageNumberUpdated', pageNumber => {
if (this.shouldDeselectAnnotationsOnPageChange) {
this.utils.deselectAllAnnotations();
}
@ -250,9 +252,9 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
});
this.instance.docViewer.on('documentLoaded', this._documentLoaded);
this.instance.Core.documentViewer.on('documentLoaded', this._documentLoaded);
this.instance.docViewer.on('keyUp', $event => {
this.instance.Core.documentViewer.on('keyUp', $event => {
// arrows and full-screen
if ($event.target?.tagName?.toLowerCase() !== 'input') {
if ($event.key.startsWith('Arrow') || $event.key === 'f') {
@ -270,23 +272,23 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
});
this.instance.docViewer.on('textSelected', (quads, selectedText) => {
this.instance.Core.documentViewer.on('textSelected', (quads, selectedText) => {
this._selectedText = selectedText;
if (selectedText.length > 2 && this.canPerformActions) {
this.instance.enableElements(['add-dictionary', 'add-false-positive']);
this.instance.UI.enableElements(['add-dictionary', 'add-false-positive']);
} else {
this.instance.disableElements(['add-dictionary', 'add-false-positive']);
this.instance.UI.disableElements(['add-dictionary', 'add-false-positive']);
}
});
this.instance.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
this.instance.UI.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
if (event.detail.element === 'searchPanel') {
const inputElement = this.instance.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
const inputElement = this.instance.UI.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
setTimeout(() => {
inputElement.value = '';
}, 0);
if (!event.detail.isVisible) {
this.instance.docViewer.clearSearchResults();
this.instance.Core.documentViewer.clearSearchResults();
}
}
});
@ -295,20 +297,20 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
private _setSelectionMode(): void {
const textTool = (<unknown>this.instance.Tools.TextTool) as TextTool;
textTool.SELECTION_MODE = this._appConfigService.getConfig(AppConfigKey.SELECTION_MODE);
const textTool = (<unknown>this.instance.Core.Tools.TextTool) as TextTool;
textTool.SELECTION_MODE = this._configService.values.SELECTION_MODE;
}
private _toggleRectangleAnnotationAction(readonly: boolean) {
if (!readonly) {
this.instance.enableElements(['add-rectangle']);
this.instance.UI.enableElements(['add-rectangle']);
} else {
this.instance.disableElements(['add-rectangle']);
this.instance.UI.disableElements(['add-rectangle']);
}
}
private _disableElements() {
this.instance.disableElements([
this.instance.UI.disableElements([
'pageNavOverlay',
'menuButton',
'selectToolButton',
@ -331,7 +333,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
'annotationGroupButton'
]);
this.instance.setHeaderItems(header => {
this.instance.UI.setHeaderItems(header => {
const originalHeaderItems = header.getItems();
originalHeaderItems.splice(8, 0, {
type: 'divider',
@ -372,9 +374,9 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
});
this.instance.disableElements(['closeCompareButton']);
this.instance.UI.disableElements(['closeCompareButton']);
this.instance.docViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({
this.instance.Core.documentViewer.getTool('AnnotationCreateRectangle').setStyles(() => ({
StrokeThickness: 2,
StrokeColor: this._annotationDrawService.getColor(this.instance, 'manual'),
FillColor: this._annotationDrawService.getColor(this.instance, 'manual'),
@ -382,13 +384,13 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}));
}
private _configureAnnotationSpecificActions(viewerAnnotations: Annotations.Annotation[]) {
private _configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) {
if (!this.canPerformActions) {
return;
}
const annotationWrappers = viewerAnnotations.map(va => this.annotations.find(a => a.id === va.Id)).filter(va => !!va);
this.instance.annotationPopup.update([]);
this.instance.UI.annotationPopup.update([]);
if (annotationWrappers.length === 0) {
this._configureRectangleAnnotationPopup();
@ -400,7 +402,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
if (allAnnotationsHaveImageAction) {
const allAreVisible = viewerAnnotations.reduce((acc, next) => next.isVisible() && acc, true);
this.instance.annotationPopup.add([
this.instance.UI.annotationPopup.add([
{
type: 'actionButton',
img: allAreVisible
@ -410,11 +412,11 @@ export class PdfViewerComponent implements OnInit, OnChanges {
onClick: () => {
this._ngZone.run(() => {
if (allAreVisible) {
this.instance.annotManager.hideAnnotations(viewerAnnotations);
this.instance.Core.annotationManager.hideAnnotations(viewerAnnotations);
} else {
this.instance.annotManager.showAnnotations(viewerAnnotations);
this.instance.Core.annotationManager.showAnnotations(viewerAnnotations);
}
this.instance.annotManager.deselectAllAnnotations();
this.instance.Core.annotationManager.deselectAllAnnotations();
this._annotationActionsService.updateHiddenAnnotation(this.annotations, viewerAnnotations, allAreVisible);
});
}
@ -422,19 +424,19 @@ export class PdfViewerComponent implements OnInit, OnChanges {
]);
}
this.instance.annotationPopup.add(
this.instance.UI.annotationPopup.add(
this._annotationActionsService.getViewerAvailableActions(annotationWrappers, this.annotationsChanged)
);
}
private _configureRectangleAnnotationPopup() {
this.instance.annotationPopup.add(<any>{
this.instance.UI.annotationPopup.add(<any>{
type: 'actionButton',
dataElement: 'add-rectangle',
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
onClick: () => {
const selectedAnnotations = this.instance.annotManager.getSelectedAnnotations();
const selectedAnnotations = this.instance.Core.annotationManager.getSelectedAnnotations();
const activeAnnotation = selectedAnnotations[0];
const activePage = selectedAnnotations[0].getPageNumber();
const quad = this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance);
@ -443,8 +445,8 @@ export class PdfViewerComponent implements OnInit, OnChanges {
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
// cleanup selection and button state
this.utils.deselectAllAnnotations();
this.instance.disableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
this.instance.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
this.instance.UI.disableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
this.instance.UI.enableElements(['shapeToolGroupButton', 'rectangleToolDivider']);
// dispatch event
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper([quad], mre, 'REDACTION', 'RECTANGLE', activeAnnotation.Id)
@ -454,12 +456,12 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
private _configureTextPopup() {
this.instance.textPopup.add(<any>{
this.instance.UI.textPopup.add(<any>{
type: 'actionButton',
img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'),
title: this._translateService.instant('pdf-viewer.text-popup.actions.search'),
onClick: () => {
const text = this.instance.docViewer.getSelectedText();
const text = this.instance.Core.documentViewer.getSelectedText();
const searchOptions = {
caseSensitive: true, // match case
wholeWord: true, // match whole words only
@ -468,57 +470,57 @@ export class PdfViewerComponent implements OnInit, OnChanges {
searchUp: false, // search from the end of the document upwards
ambientString: true // return ambient string as part of the result
};
this.instance.openElements(['searchPanel']);
this.instance.UI.openElements(['searchPanel']);
setTimeout(() => {
this.instance.searchTextFull(text, searchOptions);
this.instance.UI.searchTextFull(text, searchOptions);
}, 250);
}
});
// Adding directly to the false-positive dict is only available in dev-mode
if (this._userPreferenceService.areDevFeaturesEnabled) {
this.instance.textPopup.add(<any>{
this.instance.UI.textPopup.add(<any>{
type: 'actionButton',
dataElement: 'add-false-positive',
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('FALSE_POSITIVE')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads();
const text = this.instance.Core.documentViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'FALSE_POSITIVE')
new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'FALSE_POSITIVE')
);
}
});
}
this.instance.textPopup.add(<any>{
this.instance.UI.textPopup.add(<any>{
type: 'actionButton',
dataElement: 'add-dictionary',
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads();
const text = this.instance.Core.documentViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'DICTIONARY')
new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'DICTIONARY')
);
}
});
this.instance.textPopup.add(<any>{
this.instance.UI.textPopup.add(<any>{
type: 'actionButton',
dataElement: 'add-redaction',
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
onClick: () => {
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
const text = this.instance.docViewer.getSelectedText();
const selectedQuads = this.instance.Core.documentViewer.getSelectedTextQuads();
const text = this.instance.Core.documentViewer.getSelectedText();
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
this.manualAnnotationRequested.emit(
new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'REDACTION')
new ManualRedactionEntryWrapper(this.instance.Core.documentViewer.getSelectedTextQuads(), mre, 'REDACTION')
);
}
});
@ -526,10 +528,10 @@ export class PdfViewerComponent implements OnInit, OnChanges {
}
private _handleCustomActions() {
this.instance.setToolMode('AnnotationEdit');
this.instance.UI.setToolMode('AnnotationEdit');
if (this.canPerformActions) {
this.instance.enableTools(['AnnotationCreateRectangle']);
this.instance.enableElements([
this.instance.UI.enableTools(['AnnotationCreateRectangle']);
this.instance.UI.enableElements([
'add-redaction',
'add-rectangle',
'add-false-positive',
@ -538,11 +540,11 @@ export class PdfViewerComponent implements OnInit, OnChanges {
'annotationPopup'
]);
if (this._selectedText.length > 2) {
this.instance.enableElements(['add-dictionary', 'add-false-positive']);
this.instance.UI.enableElements(['add-dictionary', 'add-false-positive']);
}
} else {
this.instance.disableTools(['AnnotationCreateRectangle']);
this.instance.disableElements([
this.instance.UI.disableTools(['AnnotationCreateRectangle']);
this.instance.UI.disableElements([
'add-redaction',
'add-dictionary',
'add-false-positive',
@ -568,7 +570,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
private _loadDocument() {
if (this.fileData) {
this.instance.loadDocument(this.fileData, {
this.instance.UI.loadDocument(this.fileData, {
filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'
});
}

View File

@ -4,7 +4,7 @@
<mat-label>{{ 'assign-dossier-owner.dialog.single-user' | translate }}</mat-label>
<mat-select formControlName="owner">
<mat-option *ngFor="let userId of ownersSelectOptions" [value]="userId">
{{ userService.getNameForId(userId) }}
{{ userId | name }}
</mat-option>
</mat-select>
</mat-form-field>

View File

@ -1,4 +1,5 @@
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'common-mixins';
@use 'variables';
.search-container {
margin-top: 16px;
@ -16,7 +17,7 @@ redaction-team-members {
&:hover {
overflow-y: auto;
@include scroll-bar;
@include common-mixins.scroll-bar;
}
> div {
@ -51,7 +52,7 @@ redaction-team-members {
&.selected,
&:hover {
background-color: $grey-2;
background-color: variables.$grey-2;
.actions {
display: flex;

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.container {
flex-wrap: wrap;
@ -27,8 +27,8 @@
width: 16px;
height: 16px;
border-radius: 50%;
background-color: $accent;
color: $white;
background-color: variables.$accent;
color: variables.$white;
position: absolute;
right: -8px;
bottom: -2px;
@ -48,6 +48,6 @@
.see-less {
opacity: 1;
color: $primary;
color: variables.$primary;
margin-top: 16px;
}

View File

@ -1,5 +1,3 @@
@import '../../../../../assets/styles/variables';
.watermark {
margin-top: 24px;
}

View File

@ -14,7 +14,7 @@
<mat-label>{{ 'assign-owner.dialog.label' | translate: { type: data.mode } }}</mat-label>
<mat-select formControlName="singleUser">
<mat-option *ngFor="let userId of singleUsersSelectOptions" [value]="userId">
{{ userService.getNameForId(userId) }}
{{ userId | name }}
</mat-option>
</mat-select>
</mat-form-field>

View File

@ -1,4 +1,4 @@
@import '../../../../../../assets/styles/variables';
@use 'variables';
:host {
display: flex;
@ -18,7 +18,7 @@
height: 100%;
width: 1px;
padding: 0;
background-color: $separator;
background-color: variables.$separator;
}
.datepicker-wrapper {
@ -60,7 +60,7 @@
height: 50px;
object-fit: cover;
object-position: center;
border: 1px solid $grey-5;
border: 1px solid variables.$grey-5;
margin-right: 15px;
}

View File

@ -1,8 +1,8 @@
@import '../../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.instructions {
color: $grey-7;
color: variables.$grey-7;
flex: 1;
text-align: end;
}
@ -11,6 +11,6 @@
height: calc(100% - 81px) !important;
.cdk-virtual-scroll-content-wrapper .table-item > div.cell.filename span {
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
}

View File

@ -13,7 +13,7 @@ import {
import { FileManagementControllerService, FileStatus, StatusControllerService } from '@redaction/red-ui-http';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import * as moment from 'moment';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { getLeftDateTime } from '@utils/functions';
import { Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
@ -44,7 +44,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
tableColumnConfigs: TableColumnConfig<FileListItem>[];
readonly tableHeaderLabel = _('edit-dossier-dialog.deleted-documents.table-header.label');
readonly circleButtonTypes = CircleButtonTypes;
readonly deleteRetentionHours = this._appConfigService.getConfig(AppConfigKey.DELETE_RETENTION_HOURS);
readonly deleteRetentionHours = this._configService.values.DELETE_RETENTION_HOURS;
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<never>;
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
@ViewChild('deletedDateTemplate', { static: true }) deletedDateTemplate: TemplateRef<never>;
@ -57,7 +57,7 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
private readonly _fileManagementController: FileManagementControllerService,
private readonly _appStateService: AppStateService,
private readonly _loadingService: LoadingService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _dialogService: DossiersDialogService
) {
super(_injector);

View File

@ -1,5 +1,3 @@
@import '../../../../../../assets/styles/variables';
.download-includes {
margin: 16px 0 10px;
font-weight: 500;

View File

@ -1,10 +1,10 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.dialog-content {
padding: 0;
margin-top: 24px;
border-top: 1px solid $separator;
border-top: 1px solid variables.$separator;
display: flex;
height: calc(90vh - 81px);
@ -14,7 +14,7 @@
.content {
padding: 24px 32px;
overflow: auto;
@include scroll-bar;
@include common-mixins.scroll-bar;
height: calc(100% - 81px);
box-sizing: border-box;

View File

@ -1,5 +1,3 @@
@import '../../../../../../assets/styles/variables';
.watermark {
margin-top: 24px;
}

View File

@ -49,6 +49,9 @@ import { DossiersService } from './services/dossiers.service';
import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component';
import { SearchScreenComponent } from './screens/search-screen/search-screen.component';
import { EditDossierDeletedDocumentsComponent } from './dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component';
import { AnnotationsListComponent } from './components/file-workload/components/annotations-list/annotations-list.component';
import { AnnotationSourceComponent } from './components/file-workload/components/annotation-source/annotation-source.component';
import { OverlayModule } from '@angular/cdk/overlay';
const screens = [DossierListingScreenComponent, DossierOverviewScreenComponent, FilePreviewScreenComponent, SearchScreenComponent];
@ -89,6 +92,8 @@ const components = [
PageExclusionComponent,
DossierDetailsStatsComponent,
EditDossierDeletedDocumentsComponent,
AnnotationsListComponent,
AnnotationSourceComponent,
...screens,
...dialogs
@ -109,6 +114,6 @@ const services = [
@NgModule({
declarations: [...components],
providers: [...services],
imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule]
imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule, OverlayModule]
})
export class DossiersModule {}

View File

@ -1,5 +1,5 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
.file-upload-input {
display: none;
@ -9,7 +9,7 @@
&.last-opened {
> .selection-column {
padding-left: 6px !important;
border-left: 4px solid $primary;
border-left: 4px solid variables.$primary;
}
> div {
@ -19,7 +19,7 @@
> div.cell {
.error {
color: $primary;
color: variables.$primary;
}
.table-item-title {
@ -28,7 +28,7 @@
.primary-attribute {
padding-top: 6px;
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
&.extend-cols {
@ -59,7 +59,7 @@
@keyframes red-fading-background {
0% {
background-color: rgba($primary, 0.1);
background-color: rgba(variables.$primary, 0.1);
}
100% {
background-color: inherit;
@ -67,7 +67,7 @@
}
.view-mode-selection {
border-right: 1px solid $separator;
border-right: 1px solid variables.$separator;
padding-right: 16px;
margin-right: 16px !important;
display: flex;
@ -94,7 +94,7 @@
.filename {
font-weight: 600;
@include line-clamp(1);
@include common-mixins.line-clamp(1);
}
}

View File

@ -28,7 +28,7 @@ import { StatusSorter } from '@utils/sorters/status-sorter';
import { convertFiles, handleFileDrop } from '@utils/file-drop-utils';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { OnAttach, OnDetach } from '@utils/custom-route-reuse.strategy';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { ActionConfig } from '@shared/components/page-header/models/action-config.model';
import {
CircleButtonTypes,
@ -52,7 +52,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { annotationFilterChecker } from '@utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { RouterHistoryService } from '@services/router-history.service';
import { DossierWrapper } from '../../../../state/model/dossier.wrapper';
import { DossierWrapper } from '@state/model/dossier.wrapper';
import { Router } from '@angular/router';
import { FileActionService } from '../../services/file-action.service';
import StatusEnum = FileStatus.StatusEnum;
@ -106,7 +106,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private readonly _loadingService: LoadingService,
private readonly _appStateService: AppStateService,
readonly routerHistoryService: RouterHistoryService,
private readonly _appConfigService: AppConfigService,
private readonly _configService: ConfigService,
private readonly _translateService: TranslateService,
private readonly _dialogService: DossiersDialogService,
private readonly _changeDetectorRef: ChangeDetectorRef,
@ -339,7 +339,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
}
recentlyModifiedChecker = (file: FileStatusWrapper) =>
moment(file.lastUpdated).add(this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS), 'hours').isAfter(moment());
moment(file.lastUpdated).add(this._configService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
private _configureTableColumns() {
const dynamicColumns: TableColumnConfig<FileStatusWrapper>[] = [];
@ -555,7 +555,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<FileStatusW
private _createQuickFilters() {
let quickFilters = [];
if (this.entitiesService.all.filter(this.recentlyModifiedChecker).length > 0) {
const recentPeriod = this._appConfigService.getConfig(AppConfigKey.RECENT_PERIOD_IN_HOURS);
const recentPeriod = this._configService.values.RECENT_PERIOD_IN_HOURS;
quickFilters = [
{
key: 'recent',

View File

@ -88,6 +88,7 @@
<div class="vertical-line"></div>
<redaction-file-actions
#fileActions
(actionPerformed)="fileActionPerformed($event)"
[activeDocumentInfo]="viewDocumentInfo"
[activeExcludePages]="excludePages"

View File

@ -1,9 +1,9 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.vertical-line {
width: 1px;
height: 30px;
background-color: $grey-4;
background-color: variables.$grey-4;
margin: 0 16px;
}
@ -32,7 +32,7 @@
::ng-deep .right-title {
height: 70px;
display: flex;
border-bottom: 1px solid $separator;
border-bottom: 1px solid variables.$separator;
align-items: center;
justify-content: space-between;
padding: 0 24px;

View File

@ -1,7 +1,7 @@
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { AppStateService } from '@state/app-state.service';
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
import { PdfViewerComponent } from '../../components/pdf-viewer/pdf-viewer.component';
import {
AutoUnsubscribe,
@ -43,6 +43,8 @@ import { TranslateService } from '@ngx-translate/core';
import { fileStatusTranslations } from '../../translations/file-status-translations';
import { handleFilterDelta } from '@utils/filter-utils';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FileActionsComponent } from '../../components/file-actions/file-actions.component';
import Annotation = Core.Annotations.Annotation;
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@ -78,6 +80,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
static: true
})
private readonly _filterTemplate: TemplateRef<NestedFilter>;
@ViewChild('fileActions') fileActions: FileActionsComponent;
constructor(
readonly appStateService: AppStateService,
@ -131,7 +134,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
get activeViewerPage(): number {
const currentPage = this._instance?.docViewer?.getCurrentPage();
const currentPage = this._instance?.Core.documentViewer?.getCurrentPage();
if (!currentPage) {
return 0;
}
@ -257,14 +260,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.displayPDFViewer = true;
this._updateCanPerformActions();
this._subscribeToFileUpdates();
if (this.fileData?.fileStatus?.analysisRequired) {
this.fileActions.reanalyseFile();
}
}
rebuildFilters(deletePreviousAnnotations = false): void {
const startTime = new Date().getTime();
if (deletePreviousAnnotations) {
const annotationsToDelete = this._instance?.annotManager?.getAnnotationsList() || [];
const annotationsToDelete = this._instance?.Core.annotationManager?.getAnnotationsList() || [];
try {
this._instance?.annotManager?.deleteAnnotations(annotationsToDelete, {
this._instance?.Core.annotationManager?.deleteAnnotations(annotationsToDelete, {
imported: true,
force: true
});
@ -344,8 +351,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
entryWrapper,
async (response: ManualAnnotationResponse) => {
if (response?.annotationId) {
const annotation = this._instance.annotManager.getAnnotationById(response.manualRedactionEntryWrapper.rectId);
this._instance.annotManager.deleteAnnotation(annotation);
const annotation = this._instance.Core.annotationManager.getAnnotationById(
response.manualRedactionEntryWrapper.rectId
);
this._instance.Core.annotationManager.deleteAnnotation(annotation);
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
const distinctPages = entryWrapper.manualRedactionEntry.positions
.map(p => p.page)
@ -550,11 +559,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private async _doStampExcludedPages(excludedPages: number[]) {
if (excludedPages && excludedPages.length > 0) {
const document = await this._instance.docViewer.getDocument().getPDFDoc();
await clearStamps(document, this._instance.PDFNet, [...Array(this.fileData.fileStatus.numberOfPages).keys()]);
const pdfNet = this._instance.Core.PDFNet;
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
await clearStamps(document, pdfNet, [...Array(this.fileData.fileStatus.numberOfPages).keys()]);
await stampPDFPage(
document,
this._instance.PDFNet,
pdfNet,
this._translateService.instant('file-preview.excluded-from-redaction'),
25,
'courier',
@ -568,8 +578,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private async _stampExcludedPages() {
await this._doStampExcludedPages(this.fileData.fileStatus.excludedPages);
this._instance.docViewer.refreshAll();
this._instance.docViewer.updateView([this.activeViewerPage], this.activeViewerPage);
this._instance.Core.documentViewer.refreshAll();
this._instance.Core.documentViewer.updateView([this.activeViewerPage], this.activeViewerPage);
this._changeDetectorRef.detectChanges();
}
@ -595,6 +605,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private async _loadFileData(performUpdate = false): Promise<void> {
const fileData = await this._fileDownloadService.loadActiveFileData().toPromise();
if (!fileData.fileStatus?.isPending && !fileData.fileStatus?.isError) {
if (performUpdate) {
this.fileData.redactionLog = fileData.redactionLog;
@ -678,9 +689,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
private _findAndDeleteAnnotation(id: string) {
const viewerAnnotation = this._instance.annotManager.getAnnotationById(id);
const viewerAnnotation = this._instance.Core.annotationManager.getAnnotationById(id);
if (viewerAnnotation) {
this._instance.annotManager.deleteAnnotation(viewerAnnotation, {
this._instance.Core.annotationManager.deleteAnnotation(viewerAnnotation, {
imported: true,
force: true
});
@ -701,16 +712,16 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
private _getAnnotations(predicate: (value) => unknown) {
const annotations = this._instance.annotManager.getAnnotationsList();
const annotations = this._instance.Core.annotationManager.getAnnotationsList();
return predicate ? annotations.filter(predicate) : annotations;
}
private _hide(annotations: Annotations.Annotation[]): void {
this._instance.annotManager.hideAnnotations(annotations);
private _hide(annotations: Annotation[]): void {
this._instance.Core.annotationManager.hideAnnotations(annotations);
}
private _show(annotations: Annotations.Annotation[]): void {
this._instance.annotManager.showAnnotations(annotations);
private _show(annotations: Annotation[]): void {
this._instance.Core.annotationManager.showAnnotations(annotations);
}
private _setAnnotationsColor(annotations, customData: string) {

View File

@ -1,4 +1,4 @@
<section *ngIf="searchResults$ | async as searchResult">
<section *ngIf="searchResults$ | async">
<redaction-page-header
(closeAction)="routerHistoryService.navigateToLastDossiersScreen()"
[searchPlaceholder]="'search.placeholder' | translate"

View File

@ -1,9 +1,8 @@
@import 'libs/common-ui/src/assets/styles/mixins';
@import '../../../../../assets/styles/variables';
@use 'common-mixins';
:host ::ng-deep iqser-table cdk-virtual-scroll-viewport .cdk-virtual-scroll-content-wrapper .table-item > div.cell {
.highlights {
@include line-clamp(1);
@include common-mixins.line-clamp(1);
em {
background-color: #fffcc4;

View File

@ -16,8 +16,8 @@ import { RouterHistoryService } from '@services/router-history.service';
interface ListItem extends Listable {
readonly dossierId: string;
readonly filename: string;
readonly unmatched: string[] | null;
readonly highlights: { [key: string]: string[] };
readonly unmatched: readonly string[] | null;
readonly highlights: Record<string, readonly string[]>;
readonly routerLink: string;
readonly status: string;
readonly dossierName: string;
@ -25,8 +25,8 @@ interface ListItem extends Listable {
}
interface SearchInput {
query: string;
dossierIds?: string[];
readonly query: string;
readonly dossierIds?: readonly string[];
}
@Component({
@ -37,10 +37,12 @@ interface SearchInput {
export class SearchScreenComponent extends ListingComponent<ListItem> implements OnDestroy, OnInit {
readonly fileStatusTranslations = fileStatusTranslations;
readonly searchPositions = SearchPositions;
@ViewChild('filenameTemplate', { static: true }) filenameTemplate: TemplateRef<never>;
@ViewChild('statusTemplate', { static: true }) statusTemplate: TemplateRef<never>;
@ViewChild('dossierTemplate', { static: true }) dossierTemplate: TemplateRef<never>;
@ViewChild('pagesTemplate', { static: true }) pagesTemplate: TemplateRef<never>;
@ViewChild('filenameTemplate', { static: true }) readonly filenameTemplate: TemplateRef<unknown>;
@ViewChild('statusTemplate', { static: true }) readonly statusTemplate: TemplateRef<unknown>;
@ViewChild('dossierTemplate', { static: true }) readonly dossierTemplate: TemplateRef<unknown>;
@ViewChild('pagesTemplate', { static: true }) readonly pagesTemplate: TemplateRef<unknown>;
readonly tableHeaderLabel = _('search-screen.table-header');
tableColumnConfigs: TableColumnConfig<ListItem>[];
readonly search$ = new BehaviorSubject<SearchInput>(null);
@ -68,6 +70,7 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
this.filterService.addFilterGroup({
slug: 'dossiers',
label: this._translateService.instant('search-screen.filters.by-dossier'),
filterceptionPlaceholder: this._translateService.instant('search-screen.filters.search-placeholder'),
icon: 'red:folder',
filters: this._appStateService.allDossiers.map(dossier => ({
key: dossier.dossierId,
@ -118,7 +121,7 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
private _search(searchInput: SearchInput): Observable<SearchResult> {
return this._searchControllerService.search({
dossierIds: searchInput.dossierIds,
dossierIds: [...searchInput.dossierIds],
queryString: searchInput.query ?? '',
page: 1,
returnSections: false,
@ -161,7 +164,7 @@ export class SearchScreenComponent extends ListingComponent<ListItem> implements
dossierId,
unmatched: unmatchedTerms || null,
highlights,
status,
status: fileWrapper.status,
numberOfPages: fileWrapper.numberOfPages,
dossierName: this._getDossierWrapper(dossierId).dossierName,
filename: fileWrapper.filename,

View File

@ -10,7 +10,8 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { DossiersDialogService } from './dossiers-dialog.service';
import { BASE_HREF } from '../../../tokens';
import { UserService } from '@services/user.service';
import { Annotations } from '@pdftron/webviewer';
import { Core } from '@pdftron/webviewer';
import Annotation = Core.Annotations.Annotation;
@Injectable()
export class AnnotationActionsService {
@ -281,7 +282,7 @@ export class AnnotationActionsService {
return availableActions;
}
updateHiddenAnnotation(annotations: AnnotationWrapper[], viewerAnnotations: Annotations.Annotation[], hidden: boolean) {
updateHiddenAnnotation(annotations: AnnotationWrapper[], viewerAnnotations: Annotation[], hidden: boolean) {
const annotationId = (viewerAnnotations[0] as any).Tw;
const annotationToBeUpdated = annotations.find((a: AnnotationWrapper) => a.annotationId === annotationId);
annotationToBeUpdated.hidden = hidden;

View File

@ -1,10 +1,11 @@
import { Injectable } from '@angular/core';
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { Core, 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 Annotation = Core.Annotations.Annotation;
@Injectable()
export class AnnotationDrawService {
@ -14,18 +15,14 @@ export class AnnotationDrawService {
private readonly _userPreferenceService: UserPreferenceService
) {}
drawAnnotations(
activeViewer: WebViewerInstance,
annotationWrappers: AnnotationWrapper[],
hideSkipped: boolean = false,
compareMode: boolean = false
) {
const annotations = [];
annotationWrappers.forEach(annotation => {
annotations.push(this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode));
});
const annotationManager = activeViewer.annotManager;
drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[], hideSkipped = false, compareMode = false) {
if (!activeViewer) {
return;
}
const annotations = annotationWrappers.map(annotation =>
this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode)
);
const annotationManager = activeViewer.Core.annotationManager;
annotationManager.addAnnotations(annotations, { imported: true });
annotationManager.drawAnnotationsFromList(annotations);
@ -49,14 +46,14 @@ export class AnnotationDrawService {
// })
});
}
const annotationManager = activeViewer.annotManager;
const annotationManager = activeViewer.Core.annotationManager;
annotationManager.addAnnotations(sections, { imported: true });
annotationManager.drawAnnotationsFromList(sections);
}
computeSection(activeViewer: WebViewerInstance, pageNumber: number, sectionRectangle: SectionRectangle) {
const rectangleAnnot = new activeViewer.Annotations.RectangleAnnotation();
const pageHeight = activeViewer.docViewer.getPageHeight(pageNumber);
const rectangleAnnot = new activeViewer.Core.Annotations.RectangleAnnotation();
const pageHeight = activeViewer.Core.documentViewer.getPageHeight(pageNumber);
const rectangle = {
topLeft: sectionRectangle.topLeft,
page: pageNumber,
@ -75,14 +72,9 @@ export class AnnotationDrawService {
return rectangleAnnot;
}
computeAnnotation(
activeViewer: WebViewerInstance,
annotationWrapper: AnnotationWrapper,
hideSkipped: boolean = false,
compareMode: boolean = false
) {
computeAnnotation(activeViewer: WebViewerInstance, annotationWrapper: AnnotationWrapper, hideSkipped = false, compareMode = false) {
const pageNumber = compareMode ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
const highlight = new activeViewer.Annotations.TextHighlightAnnotation();
const highlight = new activeViewer.Core.Annotations.TextHighlightAnnotation();
highlight.PageNumber = pageNumber;
highlight.StrokeColor = this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type);
highlight.setContents(annotationWrapper.content);
@ -96,13 +88,16 @@ export class AnnotationDrawService {
(hideSkipped && annotationWrapper.isSkipped) ||
annotationWrapper.isOCR ||
annotationWrapper.hidden;
highlight.setCustomData('redacto-manager', true);
highlight.setCustomData('redaction', annotationWrapper.isRedacted);
highlight.setCustomData('skipped', annotationWrapper.isSkipped);
highlight.setCustomData('changeLog', annotationWrapper.isChangeLogEntry);
highlight.setCustomData('changeLogRemoved', annotationWrapper.isChangeLogRemoved);
highlight.setCustomData('redactionColor', this.getColor(activeViewer, 'redaction', 'redaction'));
highlight.setCustomData('annotationColor', this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type));
highlight.setCustomData('redacto-manager', 'true');
highlight.setCustomData('redaction', String(annotationWrapper.isRedacted));
highlight.setCustomData('skipped', String(annotationWrapper.isSkipped));
highlight.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
highlight.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
highlight.setCustomData('redactionColor', String(this.getColor(activeViewer, 'redaction', 'redaction')));
highlight.setCustomData(
'annotationColor',
String(this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.type))
);
return highlight;
}
@ -123,10 +118,10 @@ export class AnnotationDrawService {
break;
}
const rgbColor = hexToRgb(color);
return new activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
return new activeViewer.Core.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
}
annotationToQuads(annotation: Annotations.Annotation, activeViewer: WebViewerInstance) {
annotationToQuads(annotation: Annotation, activeViewer: WebViewerInstance) {
const x1 = annotation.getRect().x1;
const y1 = annotation.getRect().y1 + annotation.getRect().getHeight();
@ -139,11 +134,11 @@ export class AnnotationDrawService {
const x4 = annotation.getRect().x1;
const y4 = annotation.getRect().y1;
return new activeViewer.CoreControls.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
return new activeViewer.Core.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);
const pageHeight = activeViewer.Core.documentViewer.getPageHeight(pageNumber);
return positions.map(p => this._rectangleToQuad(p, activeViewer, pageHeight));
}
@ -160,6 +155,6 @@ export class AnnotationDrawService {
const x4 = rectangle.topLeft.x;
const y4 = pageHeight - rectangle.topLeft.y;
return new activeViewer.CoreControls.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
return new activeViewer.Core.Math.Quad(x1, y1, x2, y2, x3, y3, x4, y4);
}
}

View File

@ -10,7 +10,7 @@ import { ManualAnnotationService } from './manual-annotation.service';
import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
import { AssignReviewerApproverDialogComponent } from '../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
import { AppConfigService } from '@app-config/app-config.service';
import { ConfigService } from '@services/config.service';
import { ChangeLegalBasisDialogComponent } from '../dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
import { RecategorizeImageDialogComponent } from '../dialogs/recategorize-image-dialog/recategorize-image-dialog.component';
import { DialogService, largeDialogConfig } from '@shared/services/dialog.service';
@ -78,7 +78,7 @@ export class DossiersDialogService extends DialogService<DialogType> {
protected readonly _dialog: MatDialog,
private readonly _appStateService: AppStateService,
private readonly _manualAnnotationService: ManualAnnotationService,
private readonly _appConfigService: AppConfigService
private readonly _configService: ConfigService
) {
super(_dialog);
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { forkJoin, Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { catchError, map, tap } from 'rxjs/operators';
import {
FileManagementControllerService,
ManualRedactionControllerService,
@ -24,10 +24,14 @@ export class PdfViewerDataService {
) {}
loadActiveFileRedactionLog() {
return this._redactionLogControllerService.getRedactionLog(
this._appStateService.activeDossierId,
this._appStateService.activeFileId
);
return this._redactionLogControllerService
.getRedactionLog(this._appStateService.activeDossierId, this._appStateService.activeFileId)
.pipe(
tap(
redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page),
catchError(() => of({}))
)
);
}
loadActiveFileData(): Observable<FileDataModel> {
@ -35,7 +39,7 @@ export class PdfViewerDataService {
const fileId = this._appStateService.activeFileId;
const file$ = this.downloadOriginalFile(this._appStateService.activeFile);
const reactionLog$ = this._redactionLogControllerService.getRedactionLog(dossierId, fileId).pipe(catchError(() => of({})));
const reactionLog$ = this.loadActiveFileRedactionLog();
const redactionChangeLog$ = this._redactionLogControllerService
.getRedactionChangeLog(dossierId, fileId)
.pipe(catchError(() => of({})));

View File

@ -1,15 +1,15 @@
import { stampPDFPage } from '../../../utils/page-stamper';
import { stampPDFPage } from '@utils/page-stamper';
const processPage = async (pageNumber, document1, document2, mergedDocument, PDFNet) => {
const processPage = async (pageNumber, document1, document2, mergedDocument, pdfNet) => {
const document1PageCount = await document1.getPageCount();
if (document1PageCount >= pageNumber) {
await mergedDocument.insertPages(pageNumber * 2, document1, pageNumber, pageNumber, PDFNet.PDFDoc.InsertFlag.e_none);
await mergedDocument.insertPages(pageNumber * 2, document1, pageNumber, pageNumber, pdfNet.PDFDoc.InsertFlag.e_none);
} else {
const pageToCopy = await document2.getPage(pageNumber);
const blankPage = await mergedDocument.pageCreate(await pageToCopy.getCropBox());
await blankPage.setRotation(await pageToCopy.getRotation());
await mergedDocument.pagePushBack(blankPage);
await stampPDFPage(mergedDocument, PDFNet, '<< Compare Placeholder Page >>', 20, 'courier', 'DIAGONAL', 33, '#ffb83b', [
await stampPDFPage(mergedDocument, pdfNet, '<< Compare Placeholder Page >>', 20, 'courier', 'DIAGONAL', 33, '#ffb83b', [
await mergedDocument.getPageCount()
]);
}
@ -25,17 +25,17 @@ export const loadCompareDocumentWrapper = async (
fileStatus,
setCompareViewMode: () => void,
navigateToPage: () => void,
PDFNet
pdfNet: any
) => {
try {
const maxPageCount = Math.max(currentDocumentPageCount, compareDocumentPageCount);
for (let idx = 1; idx <= maxPageCount; idx++) {
await processPage(idx, currentDocument, compareDocument, mergedDocument, PDFNet);
await processPage(idx, compareDocument, currentDocument, mergedDocument, PDFNet);
await processPage(idx, currentDocument, compareDocument, mergedDocument, pdfNet);
await processPage(idx, compareDocument, currentDocument, mergedDocument, pdfNet);
}
const buffer = await mergedDocument.saveMemoryBuffer(PDFNet.SDFDoc.SaveOptions.e_linearized);
const buffer = await mergedDocument.saveMemoryBuffer(pdfNet.SDFDoc.SaveOptions.e_linearized);
const mergedDocumentBuffer = new Blob([buffer], {
type: 'application/pdf'

View File

@ -1,8 +1,9 @@
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
import { ViewMode } from '@models/file/view-mode';
import { translateQuads } from '../../../utils/pdf-coordinates';
import { translateQuads } from '@utils/pdf-coordinates';
import { Rectangle } from '@redaction/red-ui-http';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core, WebViewerInstance } from '@pdftron/webviewer';
import Annotation = Core.Annotations.Annotation;
const DISABLED_HOTKEYS = [
'CTRL+SHIFT+EQUAL',
@ -35,18 +36,19 @@ const DISABLED_HOTKEYS = [
'H',
'K',
'U'
];
] as const;
export class PdfViewerUtils {
instance: WebViewerInstance;
viewMode: ViewMode;
ready = false;
multiSelectActive: boolean;
constructor(instance: WebViewerInstance, viewMode: ViewMode, multiSelectActive: boolean) {
this.instance = instance;
this.viewMode = viewMode;
this.multiSelectActive = multiSelectActive;
constructor(readonly instance: WebViewerInstance, public viewMode: ViewMode, public multiSelectActive: boolean) {}
private get _documentViewer() {
return this.instance?.Core.documentViewer;
}
private get _annotationManager() {
return this.instance?.Core.annotationManager;
}
get isCompareMode() {
@ -59,9 +61,7 @@ export class PdfViewerUtils {
get currentPage() {
try {
return this.isCompareMode
? Math.ceil(this.instance?.docViewer?.getCurrentPage() / 2)
: this.instance?.docViewer?.getCurrentPage();
return this.isCompareMode ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage;
} catch (e) {
return null;
}
@ -73,18 +73,18 @@ export class PdfViewerUtils {
}
try {
return this.isCompareMode ? Math.ceil(this.instance?.docViewer?.getPageCount() / 2) : this.instance?.docViewer?.getPageCount();
return this.isCompareMode ? Math.ceil(this._totalInternalPages / 2) : this._totalInternalPages;
} catch (e) {
return null;
}
}
private get _currentInternalPage() {
return this.instance?.docViewer?.getCurrentPage();
return this.instance?.Core.documentViewer?.getCurrentPage();
}
private get _totalInternalPages() {
return this.instance?.docViewer?.getPageCount();
return this.instance?.Core.documentViewer?.getPageCount();
}
navigateToPage(pageNumber: string | number) {
@ -106,17 +106,17 @@ export class PdfViewerUtils {
disableHotkeys(): void {
for (const hotkey of DISABLED_HOTKEYS) {
this.instance.hotkeys.off(hotkey);
this.instance.UI.hotkeys.off(hotkey);
}
}
translateQuads(page: number, quads: any) {
const rotation = this.instance.docViewer.getCompleteRotation(page);
const rotation = this._documentViewer.getCompleteRotation(page);
return translateQuads(page, rotation, quads);
}
toPosition(page: number, selectedQuad: any): Rectangle {
const pageHeight = this.instance.docViewer.getPageHeight(page);
const pageHeight = this._documentViewer.getPageHeight(page);
const height = selectedQuad.y2 - selectedQuad.y4;
return {
page: page,
@ -130,7 +130,7 @@ export class PdfViewerUtils {
}
deselectAllAnnotations() {
this.instance.annotManager.deselectAllAnnotations();
this._annotationManager.deselectAllAnnotations();
}
selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
@ -149,24 +149,23 @@ export class PdfViewerUtils {
}
const annotationsFromViewer = annotations.map(ann => this._getAnnotationById(ann.id));
this.instance.annotManager.selectAnnotations(annotationsFromViewer);
this._annotationManager.selectAnnotations(annotationsFromViewer);
// this.navigateToPage(annotations[0].pageNumber*this.paginationOffset);
this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
this._annotationManager.jumpToAnnotation(annotationsFromViewer[0]);
}
deselectAnnotations(annotations: AnnotationWrapper[]) {
const ann = annotations.map(a => this._getAnnotationById(a.id));
this.instance.annotManager.deselectAnnotations(ann);
this._annotationManager.deselectAnnotations(ann);
}
private _navigateToPage(pageNumber) {
const activePage = this.instance.docViewer.getCurrentPage();
if (activePage !== pageNumber) {
this.instance.docViewer.displayPageLocation(pageNumber, 0, 0);
if (this._currentInternalPage !== pageNumber) {
this._documentViewer.displayPageLocation(pageNumber, 0, 0);
}
}
private _getAnnotationById(id: string): Annotations.Annotation {
return this.instance.annotManager.getAnnotationById(id);
private _getAnnotationById(id: string): Annotation {
return this._annotationManager.getAnnotationById(id);
}
}

View File

@ -12,6 +12,7 @@ export class IconsModule {
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
const icons = [
'add',
'ai',
'analyse',
'approved',
'arrow-right',
@ -62,6 +63,7 @@ export class IconsModule {
'reason',
'remove-from-dict',
'report',
'rule',
'secret',
'status',
'status-collapse',

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.icon {
height: 16px;
@ -11,7 +11,7 @@
align-items: center;
text-align: center;
text-transform: uppercase;
color: $white;
color: variables.$white;
position: relative;
background-color: var(--color);
}
@ -60,9 +60,9 @@
}
.skipped {
background-color: $grey-5;
background-color: variables.$grey-5;
}
.none {
color: $accent;
color: variables.$accent;
}

View File

@ -1,4 +1,4 @@
@import '../../../../../../assets/styles/variables';
@use 'variables';
:host {
button {
@ -11,7 +11,7 @@
&[aria-expanded='true'] {
button {
background: rgba($primary, 0.1);
background: rgba(variables.$primary, 0.1);
}
}

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
:host {
width: 100%;
@ -78,7 +78,7 @@ ngx-monaco-editor {
position: absolute;
right: 12px;
top: 8px;
color: $accent;
color: variables.$accent;
.with-input {
display: flex;
@ -106,7 +106,7 @@ ngx-monaco-editor {
padding: 0 100px;
box-sizing: border-box;
text-align: center;
border: 1px solid $grey-5;
border: 1px solid variables.$grey-5;
> mat-icon {
height: 60px;
@ -116,6 +116,6 @@ ngx-monaco-editor {
}
.heading-l {
color: $grey-7;
color: variables.$grey-7;
}
}

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
.wrapper {
display: flex;
@ -15,5 +15,5 @@
}
.border {
border: 1px solid $grey-7;
border: 1px solid variables.$grey-7;
}

View File

@ -1,4 +1,4 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
:host {
display: flex;
@ -22,7 +22,7 @@
}
&.active {
color: $primary;
color: variables.$primary;
font-weight: bold;
}
}

View File

@ -1,13 +1,13 @@
@import '../../../../../assets/styles/variables';
@use 'variables';
:host {
border-radius: 8px;
border: 1px solid $grey-5;
border: 1px solid variables.$grey-5;
min-height: 100%;
display: block;
&.ng-invalid {
border-color: rgba($primary, 0.3);
border-color: rgba(variables.$primary, 0.3);
}
}
@ -47,23 +47,23 @@ mat-chip {
}
.mat-chip.mat-standard-chip.mat-chip-selected.mat-primary {
background-color: $grey-6;
color: $accent;
background-color: variables.$grey-6;
color: variables.$accent;
}
.mat-chip.mat-standard-chip {
background-color: $white;
color: $accent;
background-color: variables.$white;
color: variables.$accent;
margin: 0 0 2px 0;
transition: background-color 0.2s;
&:hover {
background-color: $grey-8;
background-color: variables.$grey-8;
}
}
.mat-chip.mat-standard-chip::after {
background: $grey-8;
background: variables.$grey-8;
}
.mat-standard-chip:focus::after {

View File

@ -1,17 +1,17 @@
@import '../../../../../assets/styles/variables';
@import 'libs/common-ui/src/assets/styles/mixins';
@use 'variables';
@use 'common-mixins';
:host {
display: block;
min-width: 200px;
max-width: 200px;
background-color: $grey-2;
border-right: 1px solid $separator;
background-color: variables.$grey-2;
border-right: 1px solid variables.$separator;
box-sizing: border-box;
padding: 8px;
height: 100%;
overflow: auto;
@include no-scroll-bar;
@include common-mixins.no-scroll-bar;
.all-caps-label {
padding: 16px;
@ -26,12 +26,12 @@
font-weight: 500;
&:not(.active):hover {
background-color: rgba($primary, 0.2);
background-color: rgba(variables.$primary, 0.2);
}
&.active {
background-color: $primary;
color: $white;
background-color: variables.$primary;
color: variables.$white;
}
}
}

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