Pull request #182: Updates
Merge in RED/ui from updates to master * commit '579ef2957f2bc21eae9613464424d6347501b4a9': fix version fix conflicts, add nrwl webpack peer dependencie, reformat code update dependencies, fix eslint & husky fix not implemented rearrange entries update paths & refactor imports minor fixes undo abstract members fix eslint errors migrate from tslint to eslint remove gitkeep and package-lock.json update dependencies with backward-incompatible changes update dependencies with backward-compatible features update dependencies with backward-compatible updates add yarn as default package manager nx updates
This commit is contained in:
commit
542085b920
182
.eslintrc.json
Normal file
182
.eslintrc.json
Normal file
@ -0,0 +1,182 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nrwl/nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nrwl/nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||
"@angular-eslint/no-host-metadata-property": "error",
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||
"@angular-eslint/no-output-native": "error",
|
||||
"@angular-eslint/no-output-on-prefix": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
"@angular-eslint/no-outputs-metadata-property": "error",
|
||||
"@angular-eslint/use-lifecycle-interface": "error",
|
||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"warn",
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"]
|
||||
},
|
||||
{
|
||||
"selector": "enumMember",
|
||||
"format": ["UPPER_CASE"]
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["protected"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private", "readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"error",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"constructor-super": "error",
|
||||
"eqeqeq": ["error", "smart"],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"import/no-deprecated": "warn",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"warn",
|
||||
"dir",
|
||||
"timeLog",
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"group",
|
||||
"groupEnd",
|
||||
"table",
|
||||
"dirxml",
|
||||
"error",
|
||||
"groupCollapsed",
|
||||
"Console",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"timeStamp",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-restricted-imports": ["error", "rxjs/Rx"],
|
||||
"no-throw-literal": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"radix": "error"
|
||||
},
|
||||
"plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"rules": {
|
||||
"@angular-eslint/template/banana-in-box": "error",
|
||||
"@angular-eslint/template/no-negated-async": "error"
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin-template"]
|
||||
}
|
||||
]
|
||||
}
|
||||
61
angular.json
61
angular.json
@ -1,5 +1,20 @@
|
||||
{
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false,
|
||||
"packageManager": "yarn"
|
||||
},
|
||||
"defaultProject": "red-ui",
|
||||
"schematics": {
|
||||
"@nrwl/angular:application": {
|
||||
"unitTestRunner": "jest",
|
||||
"e2eTestRunner": "cypress"
|
||||
},
|
||||
"@nrwl/angular:library": {
|
||||
"unitTestRunner": "jest"
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"red-ui": {
|
||||
"projectType": "application",
|
||||
@ -59,7 +74,6 @@
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
@ -79,7 +93,8 @@
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "apps/red-ui/ngsw-config.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"outputs": ["{options.outputPath}"]
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
@ -99,10 +114,9 @@
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@nrwl/linter:eslint",
|
||||
"options": {
|
||||
"tsConfig": ["apps/red-ui/tsconfig.app.json"],
|
||||
"exclude": ["**/node_modules/**", "!apps/red-ui/**/*"]
|
||||
"lintFilePatterns": ["apps/red-ui/src/**/*.ts", "apps/red-ui/src/**/*.html"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@ -110,7 +124,8 @@
|
||||
"options": {
|
||||
"jestConfig": "apps/red-ui/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"outputs": ["coverage/apps/red-ui"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -121,17 +136,16 @@
|
||||
"prefix": "redaction",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"builder": "@angular-devkit/build-angular:ng-packagr",
|
||||
"options": {
|
||||
"tsConfig": "libs/red-ui-http/tsconfig.lib.json",
|
||||
"project": "libs/red-ui-http/ng-package.json"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@nrwl/linter:eslint",
|
||||
"options": {
|
||||
"tsConfig": ["libs/red-ui-http/tsconfig.lib.json"],
|
||||
"exclude": ["**/node_modules/**", "!libs/red-ui-http/**/*"]
|
||||
"lintFilePatterns": ["libs/red-ui-http/src/**/*.ts", "libs/red-ui-http/src/**/*.html"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@ -139,7 +153,8 @@
|
||||
"options": {
|
||||
"jestConfig": "libs/red-ui-http/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"outputs": ["coverage/libs/red-ui-http"]
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
@ -155,10 +170,9 @@
|
||||
"prefix": "redaction",
|
||||
"architect": {
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"builder": "@nrwl/linter:eslint",
|
||||
"options": {
|
||||
"tsConfig": ["libs/red-cache/tsconfig.lib.json", "libs/red-cache/tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**", "!libs/red-cache/**/*"]
|
||||
"lintFilePatterns": ["libs/red-cache/src/**/*.ts", "libs/red-cache/src/**/*.html"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
@ -166,7 +180,8 @@
|
||||
"options": {
|
||||
"jestConfig": "libs/red-cache/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
},
|
||||
"outputs": ["coverage/libs/red-cache"]
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
@ -175,19 +190,5 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false
|
||||
},
|
||||
"schematics": {
|
||||
"@nrwl/angular:application": {
|
||||
"unitTestRunner": "jest",
|
||||
"e2eTestRunner": "cypress"
|
||||
},
|
||||
"@nrwl/angular:library": {
|
||||
"unitTestRunner": "jest"
|
||||
}
|
||||
},
|
||||
"defaultProject": "red-ui"
|
||||
}
|
||||
}
|
||||
|
||||
37
apps/red-ui/.eslintrc.json
Normal file
37
apps/red-ui/.eslintrc.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
|
||||
"parserOptions": {
|
||||
"project": ["apps/red-ui/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "redaction",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "redaction",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nrwl/nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,21 +1,19 @@
|
||||
module.exports = {
|
||||
name: 'red-ui',
|
||||
preset: '../../jest.config.js',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
astTransformers: [
|
||||
'jest-preset-angular/build/InlineFilesTransformer',
|
||||
'jest-preset-angular/build/StripStylesTransformer'
|
||||
]
|
||||
astTransformers: { before: ['jest-preset-angular/build/InlineFilesTransformer', 'jest-preset-angular/build/StripStylesTransformer'] },
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/red-ui',
|
||||
|
||||
displayName: 'red-ui',
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/AngularSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/HTMLCommentSerializer.js'
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment'
|
||||
]
|
||||
};
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { AuthErrorComponent } from './components/auth-error/auth-error.component';
|
||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||
import { AuthGuard } from './modules/auth/auth.guard';
|
||||
import { CompositeRouteGuard } from './guards/composite-route.guard';
|
||||
import { CompositeRouteGuard } from '@guards/composite-route.guard';
|
||||
import { RedRoleGuard } from './modules/auth/red-role.guard';
|
||||
import { BaseScreenComponent } from './components/base-screen/base-screen.component';
|
||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { DownloadsListScreenComponent } from './components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { AppStateGuard } from './state/app-state.guard';
|
||||
import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component';
|
||||
import { CustomRouteReuseStrategy } from './utils/custom-route-reuse.strategy';
|
||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { AppStateGuard } from '@state/app-state.guard';
|
||||
import { UserProfileScreenComponent } from '@components/user-profile/user-profile-screen.component';
|
||||
import { CustomRouteReuseStrategy } from '@utils/custom-route-reuse.strategy';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
||||
@ -1,4 +1,2 @@
|
||||
<router-outlet></router-outlet>
|
||||
<redaction-full-page-loading-indicator
|
||||
[displayed]="appLoadStateService.loading | async"
|
||||
></redaction-full-page-loading-indicator>
|
||||
<redaction-full-page-loading-indicator [displayed]="appLoadStateService.loading | async"></redaction-full-page-loading-indicator>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { AppLoadStateService } from './services/app-load-state.service';
|
||||
import { RouterHistoryService } from './services/router-history.service';
|
||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-root',
|
||||
|
||||
@ -4,34 +4,34 @@ import { AppComponent } from './app.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
|
||||
import { BaseScreenComponent } from './components/base-screen/base-screen.component';
|
||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||
import { ApiModule } from '@redaction/red-ui-http';
|
||||
import { ApiPathInterceptor } from './utils/api-path-interceptor';
|
||||
import { ApiPathInterceptor } from '@utils/api-path-interceptor';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
||||
import { languageInitializer } from './i18n/language.initializer';
|
||||
import { LanguageService } from './i18n/language.service';
|
||||
import { languageInitializer } from '@i18n/language.initializer';
|
||||
import { LanguageService } from '@i18n/language.service';
|
||||
import { ToastrModule } from 'ngx-toastr';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
import { environment } from '../environments/environment';
|
||||
import { environment } from '@environments/environment';
|
||||
import { AuthModule } from './modules/auth/auth.module';
|
||||
import { LogoComponent } from './components/logo/logo.component';
|
||||
import { AuthErrorComponent } from './components/auth-error/auth-error.component';
|
||||
import { ToastComponent } from './components/toast/toast.component';
|
||||
import { LogoComponent } from '@components/logo/logo.component';
|
||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||
import { ToastComponent } from '@components/toast/toast.component';
|
||||
import { HttpCacheInterceptor } from '@redaction/red-cache';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
import { NotificationsComponent } from '@components/notifications/notifications.component';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { DownloadsListScreenComponent } from './components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { SharedModule } from './modules/shared/shared.module';
|
||||
import { FileUploadDownloadModule } from './modules/upload-download/file-upload-download.module';
|
||||
import { UserProfileScreenComponent } from './components/user-profile/user-profile-screen.component';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
|
||||
import { UserProfileScreenComponent } from '@components/user-profile/user-profile-screen.component';
|
||||
import { PlatformLocation } from '@angular/common';
|
||||
import { BASE_HREF } from './tokens';
|
||||
|
||||
declare var ace;
|
||||
declare let ace;
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
export function httpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ const components = [AppComponent, LogoComponent, AuthErrorComponent, ToastCompon
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
useFactory: httpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
<section>
|
||||
<p class="heading-xl" [translate]="'auth-error.heading'" *ngIf="!configuredAdminName && !configuredAdminUrl"></p>
|
||||
<p *ngIf="!configuredAdminName && !configuredAdminUrl" [translate]="'auth-error.heading'" class="heading-xl"></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }"
|
||||
*ngIf="configuredAdminName && configuredAdminUrl"
|
||||
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }"
|
||||
class="heading-xl"
|
||||
></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: configuredAdminName }"
|
||||
*ngIf="configuredAdminName && !configuredAdminUrl"
|
||||
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: configuredAdminName }"
|
||||
class="heading-xl"
|
||||
></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: configuredAdminName }"
|
||||
*ngIf="!configuredAdminName && configuredAdminUrl"
|
||||
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: configuredAdminName }"
|
||||
class="heading-xl"
|
||||
></p>
|
||||
<a (click)="logout()" [translate]="'auth-error.logout'"></a>
|
||||
</section>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { AppConfigKey, AppConfigService } from '../../modules/app-config/app-config.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-auth-error',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<div class="red-top-bar">
|
||||
<div class="top-bar-row">
|
||||
<div class="menu-placeholder" *ngIf="!permissionsService.isUser()"></div>
|
||||
<div class="menu visible-lt-lg" *ngIf="permissionsService.isUser()">
|
||||
<div *ngIf="!permissionsService.isUser()" class="menu-placeholder"></div>
|
||||
<div *ngIf="permissionsService.isUser()" class="menu visible-lt-lg">
|
||||
<button [matMenuTriggerFor]="menuNav" mat-flat-button>
|
||||
<mat-icon svgIcon="red:menu"></mat-icon>
|
||||
</button>
|
||||
@ -19,36 +19,36 @@
|
||||
</button>
|
||||
</mat-menu>
|
||||
</div>
|
||||
<div class="menu flex-2 visible-lg breadcrumbs-container" *ngIf="permissionsService.isUser()">
|
||||
<div *ngIf="permissionsService.isUser()" class="menu flex-2 visible-lg breadcrumbs-container">
|
||||
<a
|
||||
class="breadcrumb"
|
||||
routerLink="/main/projects"
|
||||
translate="top-bar.navigation-items.projects"
|
||||
routerLinkActive="active"
|
||||
*ngIf="projectsView"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
class="breadcrumb"
|
||||
routerLink="/main/projects"
|
||||
routerLinkActive="active"
|
||||
translate="top-bar.navigation-items.projects"
|
||||
></a>
|
||||
<a class="breadcrumb back" redactionNavigateLastProjectsScreen *ngIf="!projectsView">
|
||||
<a *ngIf="!projectsView" class="breadcrumb back" redactionNavigateLastProjectsScreen>
|
||||
<mat-icon svgIcon="red:expand"></mat-icon>
|
||||
{{ 'top-bar.navigation-items.back' | translate }}
|
||||
</a>
|
||||
<ng-container *ngIf="projectsView">
|
||||
<mat-icon class="primary" *ngIf="!appStateService.activeProject" svgIcon="red:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="!appStateService.activeProject" class="primary" svgIcon="red:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="appStateService.activeProject" svgIcon="red:arrow-right"></mat-icon>
|
||||
<a
|
||||
*ngIf="appStateService.activeProject"
|
||||
class="breadcrumb"
|
||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId"
|
||||
routerLinkActive="active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId"
|
||||
class="breadcrumb"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ appStateService.activeProject.project.projectName }}
|
||||
</a>
|
||||
<mat-icon svgIcon="red:arrow-right" *ngIf="appStateService.activeFile"></mat-icon>
|
||||
<mat-icon *ngIf="appStateService.activeFile" svgIcon="red:arrow-right"></mat-icon>
|
||||
<a
|
||||
*ngIf="appStateService.activeFile"
|
||||
class="breadcrumb"
|
||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
|
||||
class="breadcrumb"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ appStateService.activeFile.filename }}
|
||||
@ -60,16 +60,16 @@
|
||||
<redaction-logo></redaction-logo>
|
||||
</redaction-hidden-action>
|
||||
<div class="app-name">{{ titleService.getTitle() }}</div>
|
||||
<span class="dev-mode" *ngIf="userPreferenceService.areDevFeaturesEnabled" translate="dev-mode"></span>
|
||||
<span *ngIf="userPreferenceService.areDevFeaturesEnabled" class="dev-mode" translate="dev-mode"></span>
|
||||
</div>
|
||||
<div class="menu right flex-2">
|
||||
<redaction-notifications class="mr-8" *ngIf="userPreferenceService.areDevFeaturesEnabled"></redaction-notifications>
|
||||
<redaction-user-button [user]="user" [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot"></redaction-user-button>
|
||||
<redaction-notifications *ngIf="userPreferenceService.areDevFeaturesEnabled" class="mr-8"></redaction-notifications>
|
||||
<redaction-user-button [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot" [user]="user"></redaction-user-button>
|
||||
<mat-menu #userMenu="matMenu" class="user-menu" xPosition="before">
|
||||
<button [routerLink]="'/main/my-profile'" mat-menu-item translate="top-bar.navigation-items.my-account.children.my-profile"></button>
|
||||
<button
|
||||
*ngIf="permissionsService.isManager() || permissionsService.isUserAdmin()"
|
||||
(click)="appStateService.reset()"
|
||||
*ngIf="permissionsService.isManager() || permissionsService.isUserAdmin()"
|
||||
[routerLink]="'/main/admin'"
|
||||
mat-menu-item
|
||||
translate="top-bar.navigation-items.my-account.children.admin"
|
||||
@ -82,7 +82,7 @@
|
||||
></button>
|
||||
<button [matMenuTriggerFor]="language" mat-menu-item translate="top-bar.navigation-items.my-account.children.language.label"></button>
|
||||
<mat-menu #language="matMenu">
|
||||
<button *ngFor="let lang of languages" (click)="changeLanguage(lang)" mat-menu-item translate>
|
||||
<button (click)="changeLanguage(lang)" *ngFor="let lang of languages" mat-menu-item translate>
|
||||
top-bar.navigation-items.my-account.children.language.{{ lang }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { LanguageService } from '../../i18n/language.service';
|
||||
import { PermissionsService } from '../../services/permissions.service';
|
||||
import { UserPreferenceService } from '../../services/user-preference.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { LanguageService } from '@i18n/language.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { AppConfigService } from '../../modules/app-config/app-config.service';
|
||||
import { AppConfigService } from '@app-config/app-config.service';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { FileDownloadService } from '../../modules/upload-download/services/file-download.service';
|
||||
import { StatusOverlayService } from '../../modules/upload-download/services/status-overlay.service';
|
||||
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
@ -17,18 +17,12 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
styleUrls: ['./base-screen.component.scss']
|
||||
})
|
||||
export class BaseScreenComponent {
|
||||
private _projectsView: boolean;
|
||||
|
||||
get user() {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly userPreferenceService: UserPreferenceService,
|
||||
public readonly titleService: Title,
|
||||
public readonly fileDownloadService: FileDownloadService,
|
||||
readonly appStateService: AppStateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
readonly titleService: Title,
|
||||
readonly fileDownloadService: FileDownloadService,
|
||||
private readonly _statusOverlayService: StatusOverlayService,
|
||||
private readonly _appConfigService: AppConfigService,
|
||||
private readonly _router: Router,
|
||||
@ -41,10 +35,16 @@ export class BaseScreenComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private _projectsView: boolean;
|
||||
|
||||
get projectsView() {
|
||||
return this._projectsView;
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
get showPendingDownloadsDot() {
|
||||
return this.fileDownloadService.hasPendingDownloads;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FileDownloadService } from '../../modules/upload-download/services/file-download.service';
|
||||
import { DownloadStatusWrapper } from '../../modules/upload-download/model/download-status.wrapper';
|
||||
import { FileDownloadService } from '@upload-download/services/file-download.service';
|
||||
import { DownloadStatusWrapper } from '@upload-download/model/download-status.wrapper';
|
||||
import { DownloadControllerService } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
@ -9,17 +9,17 @@ import { DownloadControllerService } from '@redaction/red-ui-http';
|
||||
styleUrls: ['./downloads-list-screen.component.scss']
|
||||
})
|
||||
export class DownloadsListScreenComponent implements OnInit {
|
||||
constructor(public readonly fileDownloadService: FileDownloadService, private readonly _downloadControllerService: DownloadControllerService) {}
|
||||
constructor(readonly fileDownloadService: FileDownloadService, private readonly _downloadControllerService: DownloadControllerService) {}
|
||||
|
||||
get noData(): boolean {
|
||||
return this.fileDownloadService.downloads.length === 0;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.fileDownloadService.getDownloadStatus().subscribe();
|
||||
}
|
||||
|
||||
public get noData(): boolean {
|
||||
return this.fileDownloadService.downloads.length === 0;
|
||||
}
|
||||
|
||||
public async downloadItem(download: DownloadStatusWrapper) {
|
||||
async downloadItem(download: DownloadStatusWrapper) {
|
||||
await this.fileDownloadService.performDownload(download);
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-logo',
|
||||
templateUrl: './logo.component.html',
|
||||
styleUrls: ['./logo.component.scss']
|
||||
})
|
||||
export class LogoComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
export class LogoComponent {}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<redaction-circle-button [matMenuTriggerFor]="overlay" icon="red:notification" [showDot]="hasUnread"></redaction-circle-button>
|
||||
<mat-menu #overlay="matMenu" class="notifications-menu" backdropClass="notifications-backdrop" xPosition="before">
|
||||
<redaction-circle-button [matMenuTriggerFor]="overlay" [showDot]="hasUnread" icon="red:notification"></redaction-circle-button>
|
||||
<mat-menu #overlay="matMenu" backdropClass="notifications-backdrop" class="notifications-menu" xPosition="before">
|
||||
<div *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
|
||||
<div class="all-caps-label">{{ day(group) }}</div>
|
||||
<div
|
||||
class="notification"
|
||||
mat-menu-item
|
||||
*ngFor="let notification of group.notifications | sortBy: 'desc':'eventTime'"
|
||||
[class.unread]="!notification.read"
|
||||
class="notification"
|
||||
mat-menu-item
|
||||
>
|
||||
<redaction-initials-avatar></redaction-initials-avatar>
|
||||
<div class="notification-content">
|
||||
@ -14,9 +14,9 @@
|
||||
<div class="small-label mt-2">{{ eventTime(notification.eventTime) }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="dot"
|
||||
(click)="toggleRead(notification, $event)"
|
||||
[matTooltip]="(notification.read ? 'notifications.mark-unread' : 'notifications.mark-read') | translate"
|
||||
class="dot"
|
||||
matTooltipPosition="before"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@ -13,26 +13,54 @@ interface Notification {
|
||||
templateUrl: './notifications.component.html',
|
||||
styleUrls: ['./notifications.component.scss']
|
||||
})
|
||||
export class NotificationsComponent implements OnInit {
|
||||
public notifications: Notification[] = [
|
||||
{ message: 'This is a notification with longer text wrapping on multiple lines', eventTime: 1607340971000, read: false },
|
||||
export class NotificationsComponent {
|
||||
notifications: Notification[] = [
|
||||
{
|
||||
message: 'This is a notification with longer text wrapping on multiple lines',
|
||||
eventTime: 1607340971000,
|
||||
read: false
|
||||
},
|
||||
{ message: 'This is a <a>link</a>', eventTime: 1607254981000, read: true },
|
||||
{ message: 'This is a <b>notification 1</b>', eventTime: 1607254571000, read: false },
|
||||
{ message: 'Notification', eventTime: 1607385727000, read: true },
|
||||
{ message: 'Another notification', eventTime: 1606829412000, read: false }
|
||||
];
|
||||
public groupedNotifications: { dateString: string; notifications: Notification[] }[] = [];
|
||||
groupedNotifications: { dateString: string; notifications: Notification[] }[] = [];
|
||||
|
||||
constructor(private _translateService: TranslateService) {
|
||||
this._groupNotifications();
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public get hasUnread() {
|
||||
get hasUnread() {
|
||||
return this.notifications.filter((notification) => !notification.read).length > 0;
|
||||
}
|
||||
|
||||
day(group: { dateString: string; notifications: Notification[] }): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
return moment(group.notifications[0].eventTime).calendar({
|
||||
sameDay: `[${this._translateService.instant('notifications.today')}]`,
|
||||
lastDay: `[${this._translateService.instant('notifications.yesterday')}]`,
|
||||
nextDay: `[${this._translateService.instant('notifications.tomorrow')}]`,
|
||||
nextWeek: 'D MMMM',
|
||||
lastWeek: 'D MMMM',
|
||||
sameElse: 'D MMMM'
|
||||
});
|
||||
}
|
||||
|
||||
eventTime(eventTime: number): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
if (moment().isSame(eventTime, 'day')) {
|
||||
return moment(eventTime).fromNow();
|
||||
} else {
|
||||
return moment(eventTime).format('hh:mm A');
|
||||
}
|
||||
}
|
||||
|
||||
toggleRead(notification: Notification, $event) {
|
||||
$event.stopPropagation();
|
||||
notification.read = !notification.read;
|
||||
}
|
||||
|
||||
private _groupNotifications() {
|
||||
const res = {};
|
||||
for (const notification of this.notifications) {
|
||||
@ -44,30 +72,4 @@ export class NotificationsComponent implements OnInit {
|
||||
this.groupedNotifications.push({ dateString: key, notifications: res[key] });
|
||||
}
|
||||
}
|
||||
|
||||
public day(group: { dateString: string; notifications: Notification[] }): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
return moment(group.notifications[0].eventTime).calendar({
|
||||
sameDay: `[${this._translateService.instant('notifications.today')}]`,
|
||||
lastDay: `[${this._translateService.instant('notifications.yesterday')}]`,
|
||||
nextDay: `[${this._translateService.instant('notifications.tomorrow')}]`,
|
||||
nextWeek: 'D MMMM',
|
||||
lastWeek: 'D MMMM',
|
||||
sameElse: 'D MMMM'
|
||||
});
|
||||
}
|
||||
|
||||
public eventTime(eventTime: number): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
if (moment().isSame(eventTime, 'day')) {
|
||||
return moment(eventTime).fromNow();
|
||||
} else {
|
||||
return moment(eventTime).format('hh:mm A');
|
||||
}
|
||||
}
|
||||
|
||||
public toggleRead(notification: Notification, $event) {
|
||||
$event.stopPropagation();
|
||||
notification.read = !notification.read;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,20 @@
|
||||
<div class="row" [style.display]="state.value === 'inactive' ? 'none' : ''">
|
||||
<div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
|
||||
<div [style.display]="state.value === 'inactive' ? 'none' : ''" class="row">
|
||||
<div *ngIf="title" [attr.aria-label]="title" [class]="options.titleClass">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div
|
||||
*ngIf="message && options.enableHtml"
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
[class]="options.messageClass"
|
||||
[innerHTML]="message"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="message && !options.enableHtml"
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
[class]="options.messageClass"
|
||||
[attr.aria-label]="message"
|
||||
>
|
||||
<div *ngIf="message && options.enableHtml" [class]="options.messageClass" [innerHTML]="message" aria-live="polite" role="alert"></div>
|
||||
<div *ngIf="message && !options.enableHtml" [attr.aria-label]="message" [class]="options.messageClass" aria-live="polite" role="alert">
|
||||
{{ message }}
|
||||
</div>
|
||||
|
||||
<div class="actions-wrapper" *ngIf="actions && actions.length">
|
||||
<a *ngFor="let action of actions" (click)="callAction($event, action.action)">
|
||||
<div *ngIf="actions && actions.length" class="actions-wrapper">
|
||||
<a (click)="callAction($event, action.action)" *ngFor="let action of actions">
|
||||
{{ action.title }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<a *ngIf="options.closeButton" (click)="remove()" class="toast-close-button">
|
||||
<a (click)="remove()" *ngIf="options.closeButton" class="toast-close-button">
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -7,16 +7,17 @@ import { Toast, ToastPackage, ToastrService } from 'ngx-toastr';
|
||||
styleUrls: ['./toast.component.scss']
|
||||
})
|
||||
export class ToastComponent extends Toast {
|
||||
constructor(protected toastrService: ToastrService, public toastPackage: ToastPackage) {
|
||||
super(toastrService, toastPackage);
|
||||
constructor(protected readonly _toastrService: ToastrService, readonly toastPackage: ToastPackage) {
|
||||
super(_toastrService, toastPackage);
|
||||
}
|
||||
|
||||
public get actions() {
|
||||
get actions() {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return this.options.actions;
|
||||
}
|
||||
|
||||
public callAction($event: MouseEvent, action: Function) {
|
||||
callAction($event: MouseEvent, action: Function) {
|
||||
$event.stopPropagation();
|
||||
if (action) {
|
||||
action();
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
<div class="overlay-shadow"></div>
|
||||
<div class="dialog">
|
||||
<div class="dialog-header">
|
||||
<div class="heading-l" [translate]="'user-profile.title'"></div>
|
||||
<div [translate]="'user-profile.title'" class="heading-l"></div>
|
||||
</div>
|
||||
<form [formGroup]="formGroup" (submit)="save()">
|
||||
<form (submit)="save()" [formGroup]="formGroup">
|
||||
<div class="dialog-content">
|
||||
<div class="dialog-content-left">
|
||||
<div class="red-input-group required">
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { PermissionsService } from '../../services/permissions.service';
|
||||
import { LanguageService } from '../../i18n/language.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { LanguageService } from '@i18n/language.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { UserControllerService } from '@redaction/red-ui-http';
|
||||
|
||||
@ -19,12 +19,12 @@ interface ProfileModel {
|
||||
styleUrls: ['./user-profile-screen.component.scss']
|
||||
})
|
||||
export class UserProfileScreenComponent implements OnInit {
|
||||
public viewReady = false;
|
||||
public formGroup: FormGroup;
|
||||
viewReady = false;
|
||||
formGroup: FormGroup;
|
||||
private _profileModel: ProfileModel;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _userControllerService: UserControllerService,
|
||||
@ -39,10 +39,6 @@ export class UserProfileScreenComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._initializeForm();
|
||||
}
|
||||
|
||||
get languageChanged(): boolean {
|
||||
return this._profileModel['language'] !== this.formGroup.get('language').value;
|
||||
}
|
||||
@ -64,6 +60,10 @@ export class UserProfileScreenComponent implements OnInit {
|
||||
return this._translateService.langs;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._initializeForm();
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
this.viewReady = false;
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ export abstract class ComponentHasChanges implements ComponentCanDeactivate {
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
||||
constructor(private readonly translateService: TranslateService) {}
|
||||
constructor(private readonly _translateService: TranslateService) {}
|
||||
|
||||
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
||||
// if there are no pending changes, just allow deactivation; else confirm first
|
||||
@ -33,6 +33,6 @@ export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate
|
||||
: // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
|
||||
// when navigating away from your angular app, the browser will show a generic warning message
|
||||
// see http://stackoverflow.com/a/42207299/7307355
|
||||
confirm(this.translateService.instant('pending-changes-guard'));
|
||||
confirm(this._translateService.instant('pending-changes-guard'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { from, of } from 'rxjs';
|
||||
import { AppLoadStateService } from '../services/app-load-state.service';
|
||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
||||
@ -5,36 +5,34 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LanguageService {
|
||||
constructor(private translateService: TranslateService) {
|
||||
translateService.addLangs(['en', 'de']);
|
||||
constructor(private readonly _translateService: TranslateService) {
|
||||
_translateService.addLangs(['en', 'de']);
|
||||
}
|
||||
|
||||
get currentLanguage() {
|
||||
return this.translateService.currentLang;
|
||||
return this._translateService.currentLang;
|
||||
}
|
||||
|
||||
chooseAndSetInitialLanguage() {
|
||||
let defaultLang: string;
|
||||
const localStorageLang = localStorage.getItem('redaction.language');
|
||||
// const browserLang = this.translateService.getBrowserLang();
|
||||
// const browserLang = this._translateService.getBrowserLang();
|
||||
const browserLang = 'en'; // Force language to english until translations are ready
|
||||
// @ts-ignore
|
||||
if (this.translateService.getLangs().includes(localStorageLang)) {
|
||||
if (this._translateService.getLangs().includes(localStorageLang)) {
|
||||
defaultLang = localStorageLang;
|
||||
// @ts-ignore
|
||||
} else if (this.translateService.getLangs().includes(browserLang)) {
|
||||
} else if (this._translateService.getLangs().includes(browserLang)) {
|
||||
defaultLang = browserLang;
|
||||
} else {
|
||||
defaultLang = 'en';
|
||||
}
|
||||
document.documentElement.lang = defaultLang;
|
||||
this.translateService.setDefaultLang(defaultLang);
|
||||
this.translateService.use(defaultLang).subscribe(() => {});
|
||||
this._translateService.setDefaultLang(defaultLang);
|
||||
this._translateService.use(defaultLang).subscribe(() => {});
|
||||
}
|
||||
|
||||
changeLanguage(language: string) {
|
||||
localStorage.setItem('redaction.language', language);
|
||||
document.documentElement.lang = language;
|
||||
this.translateService.use(language).subscribe(() => {});
|
||||
this._translateService.use(language).subscribe(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { UserWrapper } from '../../services/user.service';
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
|
||||
export class AnnotationPermissions {
|
||||
@ -16,7 +16,17 @@ export class AnnotationPermissions {
|
||||
|
||||
canForceRedaction: boolean;
|
||||
|
||||
public static forUser(isManagerAndOwner: boolean, user: UserWrapper, annotation: AnnotationWrapper) {
|
||||
get canPerformMultipleRemoveActions() {
|
||||
return (
|
||||
<any>this.canMarkTextOnlyAsFalsePositive +
|
||||
<any>this.canMarkAsFalsePositive +
|
||||
<any>this.canRemoveOrSuggestToRemoveFromDictionary +
|
||||
<any>this.canRemoveOrSuggestToRemoveOnlyHere >=
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
static forUser(isManagerAndOwner: boolean, user: UserWrapper, annotation: AnnotationWrapper) {
|
||||
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
||||
|
||||
permissions.canUndo =
|
||||
@ -40,14 +50,4 @@ export class AnnotationPermissions {
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public get canPerformMultipleRemoveActions() {
|
||||
return (
|
||||
<any>this.canMarkTextOnlyAsFalsePositive +
|
||||
<any>this.canMarkAsFalsePositive +
|
||||
<any>this.canRemoveOrSuggestToRemoveFromDictionary +
|
||||
<any>this.canRemoveOrSuggestToRemoveOnlyHere >=
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import {
|
||||
ViewedPages
|
||||
} from '@redaction/red-ui-http';
|
||||
import { FileStatusWrapper } from './file-status.wrapper';
|
||||
import { UserWrapper } from '../../services/user.service';
|
||||
import { UserWrapper } from '@services/user.service';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||
import { ViewMode } from './view-mode';
|
||||
@ -38,9 +38,7 @@ export class FileDataModel {
|
||||
let allAnnotations = entries.map((entry) => AnnotationWrapper.fromData(entry));
|
||||
|
||||
if (!areDevFeaturesEnabled) {
|
||||
allAnnotations = allAnnotations.filter((annotation) => {
|
||||
return !annotation.isFalsePositive;
|
||||
});
|
||||
allAnnotations = allAnnotations.filter((annotation) => !annotation.isFalsePositive);
|
||||
}
|
||||
|
||||
const visibleAnnotations = allAnnotations.filter((annotation) => {
|
||||
@ -113,7 +111,7 @@ export class FileDataModel {
|
||||
}
|
||||
|
||||
// an entry for this request already exists in the redactionLog
|
||||
if (!!relevantRedactionLogEntry) {
|
||||
if (relevantRedactionLogEntry) {
|
||||
relevantRedactionLogEntry.userId = forceRedaction.user;
|
||||
relevantRedactionLogEntry.dictionaryEntry = false;
|
||||
relevantRedactionLogEntry.force = true;
|
||||
@ -132,14 +130,14 @@ export class FileDataModel {
|
||||
const relevantRedactionLogEntry = result.find((r) => r.id === manual.id);
|
||||
|
||||
// a redaction-log entry is marked as a reason for another entry - hide it
|
||||
if (!!markedAsReasonRedactionLogEntry) {
|
||||
if (markedAsReasonRedactionLogEntry) {
|
||||
if (!(this._hasAlreadyBeenProcessed(manual) && manual.status === 'APPROVED')) {
|
||||
markedAsReasonRedactionLogEntry.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
// an entry for this request already exists in the redactionLog
|
||||
if (!!relevantRedactionLogEntry) {
|
||||
if (relevantRedactionLogEntry) {
|
||||
if (relevantRedactionLogEntry.status === 'DECLINED') {
|
||||
relevantRedactionLogEntry.hidden = true;
|
||||
return;
|
||||
@ -178,7 +176,7 @@ export class FileDataModel {
|
||||
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
||||
redactionLogEntryWrapper.manual = true;
|
||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
if (!!markedAsReasonRedactionLogEntry) {
|
||||
if (markedAsReasonRedactionLogEntry) {
|
||||
// cleanup reason if the reason is another annotationId - it is not needed for drawing
|
||||
redactionLogEntryWrapper.reason = null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileStatus } from '@redaction/red-ui-http';
|
||||
import { StatusSorter } from '../../utils/sorters/status-sorter';
|
||||
import { FileAttributesConfig, FileStatus } from '@redaction/red-ui-http';
|
||||
import { StatusSorter } from '@utils/sorters/status-sorter';
|
||||
|
||||
export class FileStatusWrapper {
|
||||
primaryAttribute: string;
|
||||
|
||||
@ -2,10 +2,10 @@ import { ManualRedactionEntry } from '@redaction/red-ui-http';
|
||||
|
||||
export class ManualRedactionEntryWrapper {
|
||||
constructor(
|
||||
public readonly quads: any,
|
||||
public readonly manualRedactionEntry: ManualRedactionEntry,
|
||||
public readonly type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE',
|
||||
public readonly annotationType: 'TEXT' | 'RECTANGLE' = 'TEXT',
|
||||
public readonly rectId?: string
|
||||
readonly quads: any,
|
||||
readonly manualRedactionEntry: ManualRedactionEntry,
|
||||
readonly type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE',
|
||||
readonly annotationType: 'TEXT' | 'RECTANGLE' = 'TEXT',
|
||||
readonly rectId?: string
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Rectangle, Comment } from '@redaction/red-ui-http';
|
||||
import { Comment, Rectangle } from '@redaction/red-ui-http';
|
||||
|
||||
export interface RedactionLogEntryWrapper {
|
||||
color?: Array<number>;
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
import { CompositeRouteGuard } from '../../guards/composite-route.guard';
|
||||
import { CompositeRouteGuard } from '@guards/composite-route.guard';
|
||||
import { RedRoleGuard } from '../auth/red-role.guard';
|
||||
import { AppStateGuard } from '../../state/app-state.guard';
|
||||
import { AppStateGuard } from '@state/app-state.guard';
|
||||
import { RuleSetsListingScreenComponent } from './screens/rule-sets-listing/rule-sets-listing-screen.component';
|
||||
import { DictionaryListingScreenComponent } from './screens/dictionary-listing/dictionary-listing-screen.component';
|
||||
import { DictionaryOverviewScreenComponent } from './screens/dictionary-overview/dictionary-overview-screen.component';
|
||||
import { PendingChangesGuard } from '../../guards/can-deactivate.guard';
|
||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||
import { RulesScreenComponent } from './screens/rules/rules-screen.component';
|
||||
import { FileAttributesListingScreenComponent } from './screens/file-attributes-listing/file-attributes-listing-screen.component';
|
||||
import { WatermarkScreenComponent } from './screens/watermark/watermark-screen.component';
|
||||
|
||||
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AdminRoutingModule } from './admin-routing.module';
|
||||
import { RulesScreenComponent } from './screens/rules/rules-screen.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { RuleSetsListingScreenComponent } from './screens/rule-sets-listing/rule-sets-listing-screen.component';
|
||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<div class="menu flex-2 visible-lg breadcrumbs-container">
|
||||
<a class="breadcrumb" [routerLink]="'/main/admin/project-templates'" translate="project-templates" *ngIf="root || !!appStateService.activeRuleSet"></a>
|
||||
<a *ngIf="root || !!appStateService.activeRuleSet" [routerLink]="'/main/admin/project-templates'" class="breadcrumb" translate="project-templates"></a>
|
||||
|
||||
<ng-container *ngIf="appStateService.activeRuleSet">
|
||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||
<a
|
||||
class="breadcrumb ml-0"
|
||||
[routerLink]="'/main/admin/project-templates/' + appStateService.activeRuleSetId"
|
||||
[class.active]="!appStateService.activeDictionaryType"
|
||||
[routerLink]="'/main/admin/project-templates/' + appStateService.activeRuleSetId"
|
||||
class="breadcrumb ml-0"
|
||||
>
|
||||
{{ appStateService.activeRuleSet.name }}
|
||||
</a>
|
||||
@ -15,8 +15,8 @@
|
||||
<ng-container *ngIf="appStateService.activeDictionary">
|
||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||
<a
|
||||
class="breadcrumb ml-0"
|
||||
[routerLink]="'/main/admin/project-templates/' + appStateService.activeRuleSetId + '/dictionaries/' + appStateService.activeDictionaryType"
|
||||
class="breadcrumb ml-0"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ appStateService.activeDictionary.label }}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { UserPreferenceService } from '../../../../services/user-preference.service';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-admin-breadcrumbs',
|
||||
@ -9,11 +9,11 @@ import { PermissionsService } from '../../../../services/permissions.service';
|
||||
styleUrls: ['./admin-breadcrumbs.component.scss']
|
||||
})
|
||||
export class AdminBreadcrumbsComponent {
|
||||
@Input() public root = false;
|
||||
@Input() root = false;
|
||||
|
||||
constructor(
|
||||
public readonly userPreferenceService: UserPreferenceService,
|
||||
public readonly permissionService: PermissionsService,
|
||||
public readonly appStateService: AppStateService
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
readonly permissionService: PermissionsService,
|
||||
readonly appStateService: AppStateService
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -1,109 +1,109 @@
|
||||
<ngx-charts-chart
|
||||
[view]="[width + legendSpacing, height]"
|
||||
[showLegend]="legend"
|
||||
[legendOptions]="legendOptions"
|
||||
(legendLabelActivate)="onActivate($event)"
|
||||
(legendLabelClick)="onClick($event)"
|
||||
(legendLabelDeactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
(legendLabelClick)="onClick($event)"
|
||||
(legendLabelActivate)="onActivate($event)"
|
||||
(legendLabelDeactivate)="onDeactivate($event)"
|
||||
[legendOptions]="legendOptions"
|
||||
[showLegend]="legend"
|
||||
[view]="[width + legendSpacing, height]"
|
||||
>
|
||||
<svg:g [attr.transform]="transform" class="bar-chart chart">
|
||||
<svg:g
|
||||
ngx-charts-x-axis
|
||||
*ngIf="xAxis"
|
||||
[xScale]="xScale"
|
||||
[dims]="dims"
|
||||
[showLabel]="showXAxisLabel"
|
||||
[labelText]="xAxisLabel"
|
||||
[tickFormatting]="xAxisTickFormatting"
|
||||
(dimensionsChanged)="updateXAxisHeight($event)"
|
||||
*ngIf="xAxis"
|
||||
[dims]="dims"
|
||||
[labelText]="xAxisLabel"
|
||||
[showLabel]="showXAxisLabel"
|
||||
[tickFormatting]="xAxisTickFormatting"
|
||||
[xScale]="xScale"
|
||||
ngx-charts-x-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
ngx-charts-y-axis
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
*ngIf="yAxis"
|
||||
[yScale]="yScale"
|
||||
[dims]="dims"
|
||||
[yOrient]="yOrientLeft"
|
||||
[labelText]="yAxisLabel"
|
||||
[showGridLines]="showGridLines"
|
||||
[showLabel]="showYAxisLabel"
|
||||
[labelText]="yAxisLabel"
|
||||
[tickFormatting]="yAxisTickFormatting"
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
[yOrient]="yOrientLeft"
|
||||
[yScale]="yScale"
|
||||
ngx-charts-y-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
ngx-charts-y-axis
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
*ngIf="yAxis"
|
||||
[yScale]="yScaleLine"
|
||||
[dims]="dims"
|
||||
[yOrient]="yOrientRight"
|
||||
[labelText]="yAxisLabelRight"
|
||||
[showGridLines]="showGridLines"
|
||||
[showLabel]="showRightYAxisLabel"
|
||||
[labelText]="yAxisLabelRight"
|
||||
[tickFormatting]="yRightAxisTickFormatting"
|
||||
(dimensionsChanged)="updateYAxisWidth($event)"
|
||||
[yOrient]="yOrientRight"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-y-axis
|
||||
></svg:g>
|
||||
<svg:g
|
||||
ngx-combo-charts-series-vertical
|
||||
[xScale]="xScale"
|
||||
[yScale]="yScale"
|
||||
[colors]="colors"
|
||||
[series]="results"
|
||||
[seriesLine]="lineChart"
|
||||
[dims]="dims"
|
||||
[gradient]="gradient"
|
||||
[tooltipDisabled]="true"
|
||||
(activate)="onActivate($event)"
|
||||
(bandwidth)="updateLineWidth($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
[colors]="colors"
|
||||
[dims]="dims"
|
||||
[gradient]="gradient"
|
||||
[noBarWhenZero]="noBarWhenZero"
|
||||
(activate)="onActivate($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
(bandwidth)="updateLineWidth($event)"
|
||||
[seriesLine]="lineChart"
|
||||
[series]="results"
|
||||
[tooltipDisabled]="true"
|
||||
[xScale]="xScale"
|
||||
[yScale]="yScale"
|
||||
ngx-combo-charts-series-vertical
|
||||
></svg:g>
|
||||
</svg:g>
|
||||
<svg:g [attr.transform]="transform" class="line-chart chart">
|
||||
<svg:g>
|
||||
<svg:g *ngFor="let series of lineChart; trackBy: trackBy">
|
||||
<svg:g
|
||||
ngx-charts-line-series
|
||||
[activeEntries]="activeEntries"
|
||||
[animations]="animations"
|
||||
[colors]="colorsLine"
|
||||
[curve]="curve"
|
||||
[data]="series"
|
||||
[rangeFillOpacity]="rangeFillOpacity"
|
||||
[scaleType]="scaleType"
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
[colors]="colorsLine"
|
||||
[data]="series"
|
||||
[activeEntries]="activeEntries"
|
||||
[scaleType]="scaleType"
|
||||
[curve]="curve"
|
||||
[rangeFillOpacity]="rangeFillOpacity"
|
||||
[animations]="animations"
|
||||
ngx-charts-line-series
|
||||
/>
|
||||
</svg:g>
|
||||
|
||||
<svg:g
|
||||
ngx-charts-tooltip-area
|
||||
*ngIf="!tooltipDisabled"
|
||||
[dims]="dims"
|
||||
[xSet]="xSet"
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
[results]="combinedSeries"
|
||||
[colors]="colorsLine"
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
(hover)="updateHoveredVertical($event)"
|
||||
*ngIf="!tooltipDisabled"
|
||||
[colors]="colorsLine"
|
||||
[dims]="dims"
|
||||
[results]="combinedSeries"
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
[xScale]="xScaleLine"
|
||||
[xSet]="xSet"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-tooltip-area
|
||||
/>
|
||||
|
||||
<svg:g *ngFor="let series of lineChart">
|
||||
<svg:g
|
||||
ngx-charts-circle-series
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
(activate)="onActivate($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
[activeEntries]="activeEntries"
|
||||
[colors]="colorsLine"
|
||||
[data]="series"
|
||||
[scaleType]="scaleType"
|
||||
[visibleValue]="hoveredVertical"
|
||||
[activeEntries]="activeEntries"
|
||||
[tooltipDisabled]="tooltipDisabled"
|
||||
(activate)="onActivate($event)"
|
||||
(deactivate)="onDeactivate($event)"
|
||||
[visibleValue]="hoveredVertical"
|
||||
[xScale]="xScaleLine"
|
||||
[yScale]="yScaleLine"
|
||||
ngx-charts-circle-series
|
||||
/>
|
||||
</svg:g>
|
||||
</svg:g>
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Component, Input, ViewEncapsulation, Output, EventEmitter, ViewChild, HostListener, ContentChild, TemplateRef } from '@angular/core';
|
||||
import { Component, ContentChild, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
|
||||
import { curveLinear } from 'd3-shape';
|
||||
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
||||
import { BaseChartComponent, LineSeriesComponent, ViewDimensions, ColorHelper, calculateViewDimensions } from '@swimlane/ngx-charts';
|
||||
import { BaseChartComponent, calculateViewDimensions, ColorHelper, LineSeriesComponent, ViewDimensions } from '@swimlane/ngx-charts';
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line: component-selector
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'combo-chart-component',
|
||||
templateUrl: './combo-chart.component.html',
|
||||
styleUrls: ['./combo-chart.component.scss'],
|
||||
@ -364,9 +364,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
}
|
||||
|
||||
onActivate(item) {
|
||||
const idx = this.activeEntries.findIndex((d) => {
|
||||
return d.name === item.name && d.value === item.value && d.series === item.series;
|
||||
});
|
||||
const idx = this.activeEntries.findIndex((d) => d.name === item.name && d.value === item.value && d.series === item.series);
|
||||
if (idx > -1) {
|
||||
return;
|
||||
}
|
||||
@ -376,9 +374,7 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
}
|
||||
|
||||
onDeactivate(item) {
|
||||
const idx = this.activeEntries.findIndex((d) => {
|
||||
return d.name === item.name && d.value === item.value && d.series === item.series;
|
||||
});
|
||||
const idx = this.activeEntries.findIndex((d) => d.name === item.name && d.value === item.value && d.series === item.series);
|
||||
|
||||
this.activeEntries.splice(idx, 1);
|
||||
this.activeEntries = [...this.activeEntries];
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, Input, Output, EventEmitter, OnChanges, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { trigger, style, animate, transition } from '@angular/animations';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { formatLabel } from '@swimlane/ngx-charts';
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line: component-selector
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
selector: 'g[ngx-combo-charts-series-vertical]',
|
||||
template: `
|
||||
<svg:g
|
||||
@ -68,7 +68,7 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
||||
x: any;
|
||||
y: any;
|
||||
|
||||
ngOnChanges(changes): void {
|
||||
ngOnChanges(): void {
|
||||
this.update();
|
||||
}
|
||||
|
||||
@ -163,23 +163,21 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
||||
this.getSeriesTooltips(this.seriesLine, index);
|
||||
const lineValue = this.seriesLine[0].series[index].value;
|
||||
bar.tooltipText = `
|
||||
<span class="tooltip-label">${tooltipLabel}</span>
|
||||
<span class="tooltip-val"> Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%</span>
|
||||
<span class='tooltip-label'>${tooltipLabel}</span>
|
||||
<span class='tooltip-val'> Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%</span>
|
||||
`;
|
||||
|
||||
return bar;
|
||||
});
|
||||
}
|
||||
|
||||
getSeriesTooltips(seriesLine, index) {
|
||||
return seriesLine.map((d) => {
|
||||
return d.series[index];
|
||||
});
|
||||
return seriesLine.map((d) => d.series[index]);
|
||||
}
|
||||
|
||||
isActive(entry): boolean {
|
||||
if (!this.activeEntries) return false;
|
||||
const item = this.activeEntries.find((d) => {
|
||||
return entry.name === d.name && entry.series === d.series;
|
||||
});
|
||||
const item = this.activeEntries.find((d) => entry.name === d.name && entry.series === d.series);
|
||||
return item !== undefined;
|
||||
}
|
||||
|
||||
|
||||
@ -2,18 +2,18 @@
|
||||
<redaction-circle-button
|
||||
(action)="openDeleteRuleSetDialog($event)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:trash"
|
||||
tooltip="project-templates-listing.action.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="openEditRuleSetDialog($event)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:edit"
|
||||
tooltip="project-templates-listing.action.edit"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
|
||||
@ -17,14 +17,14 @@ export class RuleSetActionsComponent {
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _router: Router,
|
||||
public readonly permissionsService: PermissionsService
|
||||
readonly permissionsService: PermissionsService
|
||||
) {
|
||||
if (!this.ruleSetId) {
|
||||
this.ruleSetId = this._appStateService.activeRuleSetId;
|
||||
}
|
||||
}
|
||||
|
||||
public get ruleSet() {
|
||||
get ruleSet() {
|
||||
return this._appStateService.getRuleSetById(this.ruleSetId);
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
<div class="all-caps-label" [translate]="type"></div>
|
||||
<div [translate]="type" class="all-caps-label"></div>
|
||||
|
||||
<ng-container *ngFor="let item of items[type]">
|
||||
<div
|
||||
class="item"
|
||||
*ngIf="
|
||||
(!item.onlyAdmin || permissionsService.isAdmin()) &&
|
||||
(!item.onlyDevMode || userPreferenceService.areDevFeaturesEnabled) &&
|
||||
(!item.userManagerOnly || permissionsService.canManageUsers())
|
||||
"
|
||||
[routerLink]="prefix + item.screen"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="prefix + item.screen"
|
||||
class="item"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ item.label || item.screen | translate }}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { UserPreferenceService } from '../../../../services/user-preference.service';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-side-nav',
|
||||
templateUrl: './side-nav.component.html',
|
||||
styleUrls: ['./side-nav.component.scss']
|
||||
})
|
||||
export class SideNavComponent implements OnInit {
|
||||
export class SideNavComponent {
|
||||
@Input() type: 'settings' | 'project-templates';
|
||||
|
||||
public items: { [key: string]: { screen: string; onlyDevMode?: boolean; onlyAdmin?: boolean; userManagerOnly?: boolean; label?: string }[] } = {
|
||||
items: { [key: string]: { screen: string; onlyDevMode?: boolean; onlyAdmin?: boolean; userManagerOnly?: boolean; label?: string }[] } = {
|
||||
settings: [
|
||||
{ screen: 'project-templates', onlyAdmin: true },
|
||||
{ screen: 'digital-signature', onlyAdmin: true },
|
||||
@ -31,14 +31,12 @@ export class SideNavComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
public readonly userPreferenceService: UserPreferenceService,
|
||||
public readonly permissionsService: PermissionsService
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
readonly permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public get prefix() {
|
||||
if (!!this._appStateService.activeDictionaryType) {
|
||||
get prefix() {
|
||||
if (this._appStateService.activeDictionaryType) {
|
||||
return '../../';
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class="collapsed-wrapper">
|
||||
<redaction-circle-button (action)="toggleCollapse.emit()" icon="red:expand" tooltipPosition="before" tooltip="user-stats.expand"></redaction-circle-button>
|
||||
<redaction-circle-button (action)="toggleCollapse.emit()" icon="red:expand" tooltip="user-stats.expand" tooltipPosition="before"></redaction-circle-button>
|
||||
<div class="all-caps-label" translate="user-stats.title"></div>
|
||||
</div>
|
||||
|
||||
@ -16,10 +16,10 @@
|
||||
<div class="mt-44">
|
||||
<redaction-simple-doughnut-chart
|
||||
[config]="chartData"
|
||||
[strokeWidth]="15"
|
||||
[radius]="63"
|
||||
[strokeWidth]="15"
|
||||
[subtitle]="'user-stats.chart.users'"
|
||||
totalType="sum"
|
||||
direction="row"
|
||||
totalType="sum"
|
||||
></redaction-simple-doughnut-chart>
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-users-stats',
|
||||
templateUrl: './users-stats.component.html',
|
||||
styleUrls: ['./users-stats.component.scss']
|
||||
})
|
||||
export class UsersStatsComponent implements OnInit {
|
||||
@Output() public toggleCollapse = new EventEmitter();
|
||||
export class UsersStatsComponent {
|
||||
@Output() toggleCollapse = new EventEmitter();
|
||||
@Input() chartData: DoughnutChartConfig[];
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
||||
@ -13,15 +13,15 @@
|
||||
</ng-container>
|
||||
|
||||
<div class="first-row">
|
||||
<div class="red-input-group required" *ngIf="!dictionary">
|
||||
<div *ngIf="!dictionary" class="red-input-group required">
|
||||
<label translate="add-edit-dictionary.form.name"></label>
|
||||
<input formControlName="type" name="type" type="text" placeholder="{{ 'add-edit-dictionary.form.name-placeholder' | translate }}" />
|
||||
<input formControlName="type" name="type" placeholder="{{ 'add-edit-dictionary.form.name-placeholder' | translate }}" type="text" />
|
||||
<span class="hint" translate="add-edit-dictionary.form.name-hint"></span>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-75">
|
||||
<label translate="add-edit-dictionary.form.rank"></label>
|
||||
<input formControlName="rank" name="rank" type="number" placeholder="{{ 'add-edit-dictionary.form.rank-placeholder' | translate }}" />
|
||||
<input formControlName="rank" name="rank" placeholder="{{ 'add-edit-dictionary.form.rank-placeholder' | translate }}" type="number" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required">
|
||||
@ -30,19 +30,19 @@
|
||||
class="hex-color-input"
|
||||
formControlName="hexColor"
|
||||
name="hexColor"
|
||||
type="text"
|
||||
placeholder="{{ 'add-edit-dictionary.form.color-placeholder' | translate }}"
|
||||
type="text"
|
||||
/>
|
||||
<div
|
||||
class="input-icon"
|
||||
[style.background]="dictionaryForm.get('hexColor').value"
|
||||
(colorPickerChange)="dictionaryForm.get('hexColor').setValue($event)"
|
||||
[colorPicker]="dictionaryForm.get('hexColor').value"
|
||||
[cpOutputFormat]="'hex'"
|
||||
(colorPickerChange)="dictionaryForm.get('hexColor').setValue($event)"
|
||||
[style.background]="dictionaryForm.get('hexColor').value"
|
||||
class="input-icon"
|
||||
>
|
||||
<mat-icon
|
||||
svgIcon="red:color-picker"
|
||||
*ngIf="!dictionaryForm.get('hexColor').value || dictionaryForm.get('hexColor').value?.length === 0"
|
||||
svgIcon="red:color-picker"
|
||||
></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
@ -51,30 +51,30 @@
|
||||
<div class="red-input-group w-450">
|
||||
<label translate="add-edit-dictionary.form.description"></label>
|
||||
<textarea
|
||||
redactionHasScrollbar
|
||||
rows="4"
|
||||
formControlName="description"
|
||||
name="description"
|
||||
type="text"
|
||||
placeholder="{{ 'add-edit-dictionary.form.description-placeholder' | translate }}"
|
||||
redactionHasScrollbar
|
||||
rows="4"
|
||||
type="text"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group slider-row">
|
||||
<mat-button-toggle-group name="hint" formControlName="hint" appearance="legacy">
|
||||
<mat-button-toggle-group appearance="legacy" formControlName="hint" name="hint">
|
||||
<mat-button-toggle [value]="false"> {{ 'add-edit-dictionary.form.redaction' | translate }}</mat-button-toggle>
|
||||
<mat-button-toggle [value]="true"> {{ 'add-edit-dictionary.form.hint' | translate }}</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<mat-checkbox name="caseSensitive" formControlName="caseSensitive" color="primary">
|
||||
<mat-checkbox color="primary" formControlName="caseSensitive" name="caseSensitive">
|
||||
{{ 'add-edit-dictionary.form.case-sensitive' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<mat-checkbox name="addToDictionaryAction" formControlName="addToDictionaryAction" color="primary">
|
||||
<mat-checkbox color="primary" formControlName="addToDictionaryAction" name="addToDictionaryAction">
|
||||
{{ 'add-edit-dictionary.form.add-to-dictionary-action' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -87,5 +87,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
@ -14,7 +14,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
})
|
||||
export class AddEditDictionaryDialogComponent {
|
||||
dictionaryForm: FormGroup;
|
||||
public readonly dictionary: TypeValue;
|
||||
readonly dictionary: TypeValue;
|
||||
private readonly _ruleSetId: string;
|
||||
|
||||
constructor(
|
||||
@ -39,11 +39,11 @@ export class AddEditDictionaryDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public get dictCaseSensitive() {
|
||||
get dictCaseSensitive() {
|
||||
return this.dictionary ? !this.dictionary.caseInsensitive : false;
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
if (!this.dictionary) return true;
|
||||
|
||||
for (const key of Object.keys(this.dictionaryForm.getRawValue())) {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-file-attribute.form.name"></label>
|
||||
<input formControlName="label" name="label" type="text" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" />
|
||||
<input formControlName="label" name="label" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-300">
|
||||
@ -15,8 +15,8 @@
|
||||
<input
|
||||
formControlName="csvColumnHeader"
|
||||
name="csvColumnHeader"
|
||||
type="text"
|
||||
placeholder="{{ 'add-edit-file-attribute.form.column-header-placeholder' | translate }}"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
|
||||
<div class="options-wrapper">
|
||||
<div class="red-input-group">
|
||||
<mat-slide-toggle formControlName="readonly" color="primary">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
|
||||
<mat-slide-toggle color="primary" formControlName="readonly">{{ 'add-edit-file-attribute.form.read-only' | translate }}</mat-slide-toggle>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group mt-0">
|
||||
<mat-checkbox name="primaryAttribute" formControlName="primaryAttribute" color="primary">
|
||||
<mat-checkbox color="primary" formControlName="primaryAttribute" name="primaryAttribute">
|
||||
{{ 'add-edit-file-attribute.form.primary' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -48,5 +48,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
|
||||
@ -10,10 +10,10 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
styleUrls: ['./add-edit-file-attribute-dialog.component.scss']
|
||||
})
|
||||
export class AddEditFileAttributeDialogComponent {
|
||||
public fileAttributeForm: FormGroup;
|
||||
public fileAttribute: FileAttributeConfig;
|
||||
public ruleSetId: string;
|
||||
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
||||
fileAttributeForm: FormGroup;
|
||||
fileAttribute: FileAttributeConfig;
|
||||
ruleSetId: string;
|
||||
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -33,7 +33,7 @@ export class AddEditFileAttributeDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
if (!this.fileAttribute) return true;
|
||||
|
||||
for (const key of Object.keys(this.fileAttributeForm.getRawValue())) {
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-project-template.form.name"></label>
|
||||
<input formControlName="name" name="name" type="text" placeholder="{{ 'add-edit-project-template.form.name-placeholder' | translate }}" />
|
||||
<input formControlName="name" name="name" placeholder="{{ 'add-edit-project-template.form.name-placeholder' | translate }}" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group w-400">
|
||||
@ -15,19 +15,19 @@
|
||||
<textarea
|
||||
formControlName="description"
|
||||
name="description"
|
||||
type="text"
|
||||
rows="4"
|
||||
placeholder="{{ 'add-edit-project-template.form.description-placeholder' | translate }}"
|
||||
rows="4"
|
||||
type="text"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="validity">
|
||||
<div>
|
||||
<mat-checkbox [checked]="hasValidFrom" (change)="hasValidFrom = !hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||
<mat-checkbox (change)="hasValidFrom = !hasValidFrom" [checked]="hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-project-template.form.valid-from' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
<mat-checkbox [checked]="hasValidTo" (change)="hasValidTo = !hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||
<mat-checkbox (change)="hasValidTo = !hasValidTo" [checked]="hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||
{{ 'add-edit-project-template.form.valid-to' | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -35,8 +35,8 @@
|
||||
<div>
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<ng-container *ngIf="hasValidFrom">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="fromPicker" formControlName="validFrom" />
|
||||
<mat-datepicker-toggle matSuffix [for]="fromPicker">
|
||||
<input [matDatepicker]="fromPicker" formControlName="validFrom" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
@ -45,8 +45,8 @@
|
||||
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<ng-container *ngIf="hasValidTo">
|
||||
<input placeholder="dd/mm/yy" [matDatepicker]="toPicker" formControlName="validTo" />
|
||||
<mat-datepicker-toggle matSuffix [for]="toPicker">
|
||||
<input [matDatepicker]="toPicker" formControlName="validTo" placeholder="dd/mm/yy" />
|
||||
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
@ -58,17 +58,17 @@
|
||||
<p class="download-includes">{{ 'download-includes' | translate }}</p>
|
||||
<div class="space-between">
|
||||
<redaction-select
|
||||
class="w-410"
|
||||
[label]="'report-type.label' | translate: { length: this.ruleSetForm.controls['reportTypes'].value.length }"
|
||||
[options]="reportTypesEnum"
|
||||
[translatePrefix]="'report-type.'"
|
||||
[label]="'report-type.label' | translate: { length: this.ruleSetForm.controls['reportTypes'].value.length }"
|
||||
class="w-410"
|
||||
formControlName="reportTypes"
|
||||
></redaction-select>
|
||||
<redaction-select
|
||||
class="w-410"
|
||||
[label]="'download-type.label' | translate: { length: this.ruleSetForm.controls['downloadFileTypes'].value.length }"
|
||||
[options]="downloadTypesEnum"
|
||||
[translatePrefix]="'download-type.'"
|
||||
[label]="'download-type.label' | translate: { length: this.ruleSetForm.controls['downloadFileTypes'].value.length }"
|
||||
class="w-410"
|
||||
formControlName="downloadFileTypes"
|
||||
></redaction-select>
|
||||
</div>
|
||||
@ -81,5 +81,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as moment from 'moment';
|
||||
import { Moment } from 'moment';
|
||||
import { RuleSetControllerService, RuleSetModel } from '@redaction/red-ui-http';
|
||||
import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils';
|
||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-rule-set-dialog',
|
||||
@ -13,11 +13,11 @@ import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils';
|
||||
styleUrls: ['./add-edit-rule-set-dialog.component.scss']
|
||||
})
|
||||
export class AddEditRuleSetDialogComponent {
|
||||
public ruleSetForm: FormGroup;
|
||||
public hasValidFrom: boolean;
|
||||
public hasValidTo: boolean;
|
||||
public downloadTypesEnum = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
public reportTypesEnum = Object.values(RuleSetModel.ReportTypesEnum);
|
||||
ruleSetForm: FormGroup;
|
||||
hasValidFrom: boolean;
|
||||
hasValidTo: boolean;
|
||||
downloadTypesEnum = ['ORIGINAL', 'PREVIEW', 'REDACTED'];
|
||||
reportTypesEnum = Object.values(RuleSetModel.ReportTypesEnum);
|
||||
|
||||
private _previousValidFrom: Moment;
|
||||
private _previousValidTo: Moment;
|
||||
@ -53,29 +53,7 @@ export class AddEditRuleSetDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
private _applyValidityIntervalConstraints(value): boolean {
|
||||
if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._previousValidFrom = this.ruleSetForm.get('validFrom').value;
|
||||
this._previousValidTo = this.ruleSetForm.get('validTo').value;
|
||||
return false;
|
||||
}
|
||||
|
||||
private _requiredIfValidator(predicate) {
|
||||
return (formControl) => {
|
||||
if (!formControl.parent) {
|
||||
return null;
|
||||
}
|
||||
if (predicate()) {
|
||||
return Validators.required(formControl);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
if (!this.ruleSet) return true;
|
||||
|
||||
for (const key of Object.keys(this.ruleSetForm.getRawValue())) {
|
||||
@ -113,4 +91,26 @@ export class AddEditRuleSetDialogComponent {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this.dialogRef.close({ ruleSet });
|
||||
}
|
||||
|
||||
private _applyValidityIntervalConstraints(value): boolean {
|
||||
if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this._previousValidFrom = this.ruleSetForm.get('validFrom').value;
|
||||
this._previousValidTo = this.ruleSetForm.get('validTo').value;
|
||||
return false;
|
||||
}
|
||||
|
||||
private _requiredIfValidator(predicate) {
|
||||
return (formControl) => {
|
||||
if (!formControl.parent) {
|
||||
return null;
|
||||
}
|
||||
if (predicate()) {
|
||||
return Validators.required(formControl);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
<div class="red-input-group">
|
||||
<label translate="add-edit-user.form.role"></label>
|
||||
<div class="roles-wrapper">
|
||||
<mat-checkbox [formControlName]="role" *ngFor="let role of ROLES" color="primary">
|
||||
<mat-checkbox *ngFor="let role of ROLES" [formControlName]="role" color="primary">
|
||||
{{ 'roles.' + role | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -40,11 +40,11 @@
|
||||
{{ (user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate }}
|
||||
</button>
|
||||
|
||||
<redaction-icon-button *ngIf="user" icon="red:trash" type="show-bg" (action)="delete()" text="add-edit-user.actions.delete"></redaction-icon-button>
|
||||
<redaction-icon-button (action)="delete()" *ngIf="user" icon="red:trash" text="add-edit-user.actions.delete" type="show-bg"></redaction-icon-button>
|
||||
|
||||
<div class="all-caps-label cancel" mat-dialog-close translate="add-edit-user.actions.cancel"></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User } from '@redaction/red-ui-http';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-user-dialog',
|
||||
@ -10,9 +10,9 @@ import { UserService } from '../../../../services/user.service';
|
||||
styleUrls: ['./add-edit-user-dialog.component.scss']
|
||||
})
|
||||
export class AddEditUserDialogComponent {
|
||||
public userForm: FormGroup;
|
||||
public ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
private ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||
userForm: FormGroup;
|
||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
@ -28,9 +28,10 @@ export class AddEditUserDialogComponent {
|
||||
value: this.user && this.user.roles.indexOf(role) !== -1,
|
||||
disabled:
|
||||
this.user &&
|
||||
Object.keys(this.ROLE_REQUIREMENTS).reduce((value, key) => {
|
||||
return value || (role === this.ROLE_REQUIREMENTS[key] && this.user.roles.indexOf(key) !== -1);
|
||||
}, false)
|
||||
Object.keys(this._ROLE_REQUIREMENTS).reduce(
|
||||
(value, key) => value || (role === this._ROLE_REQUIREMENTS[key] && this.user.roles.indexOf(key) !== -1),
|
||||
false
|
||||
)
|
||||
}
|
||||
]
|
||||
}),
|
||||
@ -46,20 +47,7 @@ export class AddEditUserDialogComponent {
|
||||
this._setRolesRequirements();
|
||||
}
|
||||
|
||||
private _setRolesRequirements() {
|
||||
for (const key of Object.keys(this.ROLE_REQUIREMENTS)) {
|
||||
this.userForm.controls[key].valueChanges.subscribe((checked) => {
|
||||
if (checked) {
|
||||
this.userForm.patchValue({ [this.ROLE_REQUIREMENTS[key]]: true });
|
||||
this.userForm.controls[this.ROLE_REQUIREMENTS[key]].disable();
|
||||
} else {
|
||||
this.userForm.controls[this.ROLE_REQUIREMENTS[key]].enable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
if (!this.user) return true;
|
||||
|
||||
for (const key of Object.keys(this.userForm.getRawValue())) {
|
||||
@ -71,18 +59,7 @@ export class AddEditUserDialogComponent {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async save() {
|
||||
this.dialogRef.close({
|
||||
action: this.user ? 'UPDATE' : 'CREATE',
|
||||
user: { ...this.userForm.getRawValue(), roles: this.activeRoles }
|
||||
});
|
||||
}
|
||||
|
||||
public async delete() {
|
||||
this.dialogRef.close('DELETE');
|
||||
}
|
||||
|
||||
public get activeRoles(): string[] {
|
||||
get activeRoles(): string[] {
|
||||
return this.ROLES.reduce((acc, role) => {
|
||||
if (this.userForm.get(role).value) {
|
||||
acc.push(role);
|
||||
@ -90,4 +67,28 @@ export class AddEditUserDialogComponent {
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.dialogRef.close({
|
||||
action: this.user ? 'UPDATE' : 'CREATE',
|
||||
user: { ...this.userForm.getRawValue(), roles: this.activeRoles }
|
||||
});
|
||||
}
|
||||
|
||||
async delete() {
|
||||
this.dialogRef.close('DELETE');
|
||||
}
|
||||
|
||||
private _setRolesRequirements() {
|
||||
for (const key of Object.keys(this._ROLE_REQUIREMENTS)) {
|
||||
this.userForm.controls[key].valueChanges.subscribe((checked) => {
|
||||
if (checked) {
|
||||
this.userForm.patchValue({ [this._ROLE_REQUIREMENTS[key]]: true });
|
||||
this.userForm.controls[this._ROLE_REQUIREMENTS[key]].disable();
|
||||
} else {
|
||||
this.userForm.controls[this._ROLE_REQUIREMENTS[key]].enable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{{ 'confirm-delete-file-attribute.title.' + type | translate: { name: fileAttribute?.label } }}
|
||||
</div>
|
||||
|
||||
<div class="inline-dialog-toast toast-error" *ngIf="showToast">
|
||||
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
||||
<div translate="confirm-delete-file-attribute.toast-error"></div>
|
||||
<a (click)="showToast = false" class="toast-close-button">
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
@ -16,18 +16,18 @@
|
||||
<mat-checkbox
|
||||
*ngFor="let checkbox of checkboxes; let idx = index"
|
||||
[(ngModel)]="checkbox.value"
|
||||
color="primary"
|
||||
[class.error]="!checkbox.value && showToast"
|
||||
color="primary"
|
||||
>
|
||||
{{ 'confirm-delete-file-attribute.' + checkbox.label | translate }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button (click)="deleteFileAttribute()">
|
||||
<button (click)="deleteFileAttribute()" color="primary" mat-flat-button>
|
||||
{{ 'confirm-delete-file-attribute.delete.' + type | translate }}
|
||||
</button>
|
||||
<div class="all-caps-label cancel" (click)="cancel()" [translate]="'confirm-delete-file-attribute.cancel.' + type"></div>
|
||||
<div (click)="cancel()" [translate]="'confirm-delete-file-attribute.cancel.' + type" class="all-caps-label cancel"></div>
|
||||
</div>
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
|
||||
@ -9,12 +9,12 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
styleUrls: ['./confirm-delete-file-attribute-dialog.component.scss']
|
||||
})
|
||||
export class ConfirmDeleteFileAttributeDialogComponent {
|
||||
public fileAttribute: FileAttributeConfig;
|
||||
public checkboxes = [
|
||||
fileAttribute: FileAttributeConfig;
|
||||
checkboxes = [
|
||||
{ value: false, label: 'impacted-documents.' + this.type },
|
||||
{ value: false, label: 'lost-details' }
|
||||
];
|
||||
public showToast = false;
|
||||
showToast = false;
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -24,11 +24,15 @@ export class ConfirmDeleteFileAttributeDialogComponent {
|
||||
this.fileAttribute = data;
|
||||
}
|
||||
|
||||
public get valid() {
|
||||
get valid() {
|
||||
return this.checkboxes[0].value && this.checkboxes[1].value;
|
||||
}
|
||||
|
||||
public deleteFileAttribute() {
|
||||
get type(): 'bulk' | 'single' {
|
||||
return !this.fileAttribute ? 'bulk' : 'single';
|
||||
}
|
||||
|
||||
deleteFileAttribute() {
|
||||
if (this.valid) {
|
||||
this.dialogRef.close(true);
|
||||
} else {
|
||||
@ -36,11 +40,7 @@ export class ConfirmDeleteFileAttributeDialogComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public get type(): 'bulk' | 'single' {
|
||||
return !this.fileAttribute ? 'bulk' : 'single';
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<section class="dialog">
|
||||
<div class="dialog-header heading-l" [translate]="'confirm-delete-users.title.' + type"></div>
|
||||
<div [translate]="'confirm-delete-users.title.' + type" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="inline-dialog-toast toast-error" *ngIf="showToast">
|
||||
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
||||
<div translate="confirm-delete-users.toast-error"></div>
|
||||
<a (click)="showToast = false" class="toast-close-button">
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
@ -14,18 +14,18 @@
|
||||
<mat-checkbox
|
||||
*ngFor="let checkbox of checkboxes; let idx = index"
|
||||
[(ngModel)]="checkbox.value"
|
||||
color="primary"
|
||||
[class.error]="!checkbox.value && showToast"
|
||||
color="primary"
|
||||
>
|
||||
{{ 'confirm-delete-users.' + checkbox.label | translate: { projectsCount: projectsCount } }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button (click)="deleteUser()">
|
||||
<button (click)="deleteUser()" color="primary" mat-flat-button>
|
||||
{{ 'confirm-delete-users.delete.' + type | translate }}
|
||||
</button>
|
||||
<div class="all-caps-label cancel" (click)="cancel()" [translate]="'confirm-delete-users.cancel.' + type"></div>
|
||||
<div (click)="cancel()" [translate]="'confirm-delete-users.cancel.' + type" class="all-caps-label cancel"></div>
|
||||
</div>
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { User } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-confirm-delete-users-dialog',
|
||||
templateUrl: './confirm-delete-users-dialog.component.html',
|
||||
styleUrls: ['./confirm-delete-users-dialog.component.scss']
|
||||
})
|
||||
export class ConfirmDeleteUsersDialogComponent implements OnInit {
|
||||
public checkboxes = [
|
||||
export class ConfirmDeleteUsersDialogComponent {
|
||||
checkboxes = [
|
||||
{ value: false, label: 'impacted-projects' },
|
||||
{ value: false, label: 'impacted-documents.' + this.type }
|
||||
];
|
||||
public showToast = false;
|
||||
public projectsCount: number;
|
||||
showToast = false;
|
||||
projectsCount: number;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public users: User[],
|
||||
@ -31,7 +31,13 @@ export class ConfirmDeleteUsersDialogComponent implements OnInit {
|
||||
}).length;
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
get valid() {
|
||||
return this.checkboxes[0].value && this.checkboxes[1].value;
|
||||
}
|
||||
|
||||
get type(): 'bulk' | 'single' {
|
||||
return this.users.length > 1 ? 'bulk' : 'single';
|
||||
}
|
||||
|
||||
async deleteUser() {
|
||||
if (this.valid) {
|
||||
@ -41,15 +47,7 @@ export class ConfirmDeleteUsersDialogComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public get valid() {
|
||||
return this.checkboxes[0].value && this.checkboxes[1].value;
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
public get type(): 'bulk' | 'single' {
|
||||
return this.users.length > 1 ? 'bulk' : 'single';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<section class="dialog">
|
||||
<div class="dialog-header heading-l" [translate]="'default-colors-screen.types.' + this.colorKey"></div>
|
||||
<div [translate]="'default-colors-screen.types.' + this.colorKey" class="dialog-header heading-l"></div>
|
||||
|
||||
<form (submit)="saveColors()" [formGroup]="colorForm">
|
||||
<div class="dialog-content">
|
||||
@ -9,17 +9,17 @@
|
||||
class="hex-color-input"
|
||||
formControlName="color"
|
||||
name="color"
|
||||
type="text"
|
||||
placeholder="{{ 'edit-color-dialog.form.color-placeholder' | translate }}"
|
||||
type="text"
|
||||
/>
|
||||
<div
|
||||
class="input-icon"
|
||||
[style.background]="colorForm.get('color').value"
|
||||
(colorPickerChange)="colorForm.get('color').setValue($event)"
|
||||
[colorPicker]="colorForm.get('color').value"
|
||||
[cpOutputFormat]="'hex'"
|
||||
(colorPickerChange)="colorForm.get('color').setValue($event)"
|
||||
[style.background]="colorForm.get('color').value"
|
||||
class="input-icon"
|
||||
>
|
||||
<mat-icon svgIcon="red:color-picker" *ngIf="!colorForm.get('color').value || colorForm.get('color').value?.length === 0"></mat-icon>
|
||||
<mat-icon *ngIf="!colorForm.get('color').value || colorForm.get('color').value?.length === 0" svgIcon="red:color-picker"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,5 +31,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@ -11,11 +11,11 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
styleUrls: ['./edit-color-dialog.component.scss']
|
||||
})
|
||||
export class EditColorDialogComponent {
|
||||
public readonly colors: Colors;
|
||||
public readonly colorKey: string;
|
||||
readonly colors: Colors;
|
||||
readonly colorKey: string;
|
||||
colorForm: FormGroup;
|
||||
private readonly _initialColor: string;
|
||||
private readonly _ruleSetId: string;
|
||||
public colorForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
@ -35,7 +35,7 @@ export class EditColorDialogComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
return this.colorForm.get('color').value !== this._initialColor;
|
||||
}
|
||||
|
||||
|
||||
@ -13,39 +13,39 @@
|
||||
<ng-container *ngIf="areSomeEntitiesSelected">
|
||||
<redaction-circle-button
|
||||
[matMenuTriggerFor]="readOnlyMenu"
|
||||
icon="red:read-only"
|
||||
tooltip="file-attributes-csv-import.table-header.actions.read-only"
|
||||
type="dark-bg"
|
||||
icon="red:read-only"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="deactivateSelection()"
|
||||
icon="red:trash"
|
||||
tooltip="file-attributes-csv-import.table-header.actions.remove-selected"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<div class="separator"></div>
|
||||
|
||||
<redaction-chevron-button text="file-attributes-csv-import.table-header.actions.type" [matMenuTriggerFor]="typeMenu"></redaction-chevron-button>
|
||||
<redaction-chevron-button [matMenuTriggerFor]="typeMenu" text="file-attributes-csv-import.table-header.actions.type"></redaction-chevron-button>
|
||||
|
||||
<mat-menu #readOnlyMenu="matMenu" class="no-padding-bottom">
|
||||
<button
|
||||
mat-menu-item
|
||||
(click)="setAttributeForSelection('readonly', true)"
|
||||
mat-menu-item
|
||||
translate="file-attributes-csv-import.table-header.actions.enable-read-only"
|
||||
></button>
|
||||
<button
|
||||
mat-menu-item
|
||||
(click)="setAttributeForSelection('readonly', false)"
|
||||
mat-menu-item
|
||||
translate="file-attributes-csv-import.table-header.actions.disable-read-only"
|
||||
></button>
|
||||
</mat-menu>
|
||||
|
||||
<mat-menu #typeMenu="matMenu" class="no-padding-bottom">
|
||||
<button *ngFor="let type of typeOptions" mat-menu-item (click)="setAttributeForSelection('type', type)">
|
||||
<button (click)="setAttributeForSelection('type', type)" *ngFor="let type of typeOptions" mat-menu-item>
|
||||
{{ 'file-attribute-types.' + type | translate }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
@ -55,71 +55,71 @@
|
||||
<div class="table-header" redactionSyncWidth="table-item">
|
||||
<div class="select-oval-placeholder"></div>
|
||||
|
||||
<redaction-table-col-name label="file-attributes-csv-import.table-col-names.name" class="name"></redaction-table-col-name>
|
||||
<redaction-table-col-name class="name" label="file-attributes-csv-import.table-col-names.name"></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="file-attributes-csv-import.table-col-names.type"></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="file-attributes-csv-import.table-col-names.read-only"
|
||||
class="flex-center"
|
||||
label="file-attributes-csv-import.table-col-names.read-only"
|
||||
leftIcon="red:read-only"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
class="flex-center"
|
||||
label="file-attributes-csv-import.table-col-names.primary"
|
||||
rightIcon="red:status-info"
|
||||
rightIconTooltip="file-attributes-csv-import.table-col-names.primary-info-tooltip"
|
||||
class="flex-center"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<div></div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:attribute" screen="file-attributes-csv-import"> </redaction-empty-state>
|
||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:attribute" screen="file-attributes-csv-import"></redaction-empty-state>
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
|
||||
<!-- Table lines -->
|
||||
<div
|
||||
class="table-item"
|
||||
*cdkVirtualFor="let field of displayedEntities"
|
||||
(mouseenter)="setHoveredColumn.emit(field.csvColumn)"
|
||||
(mouseleave)="setHoveredColumn.emit()"
|
||||
*cdkVirtualFor="let field of displayedEntities"
|
||||
class="table-item"
|
||||
>
|
||||
<div class="selection-column" (click)="toggleEntitySelected($event, field)">
|
||||
<div (click)="toggleEntitySelected($event, field)" class="selection-column">
|
||||
<redaction-round-checkbox [active]="isEntitySelected(field)"></redaction-round-checkbox>
|
||||
</div>
|
||||
<div class="name" [class.editing]="field.editingName">
|
||||
<div [class.editing]="field.editingName" class="name">
|
||||
<div *ngIf="!field.editingName">
|
||||
{{ field.name }}
|
||||
</div>
|
||||
<form (submit)="field.editingName = false; field.name = field.temporaryName" *ngIf="field.editingName">
|
||||
<div class="red-input-group w-200">
|
||||
<input name="name" [(ngModel)]="field.temporaryName" />
|
||||
<input [(ngModel)]="field.temporaryName" name="name" />
|
||||
</div>
|
||||
</form>
|
||||
<redaction-circle-button
|
||||
class="edit-name-button"
|
||||
*ngIf="!field.editingName"
|
||||
(action)="field.editingName = true"
|
||||
*ngIf="!field.editingName"
|
||||
class="edit-name-button"
|
||||
icon="red:edit"
|
||||
tooltip="file-attributes-csv-import.action.edit-name"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
<ng-container *ngIf="field.editingName">
|
||||
<redaction-circle-button
|
||||
(action)="field.editingName = false; field.name = field.temporaryName"
|
||||
icon="red:check"
|
||||
tooltip="file-attributes-csv-import.action.save-name"
|
||||
type="dark-bg"
|
||||
icon="red:check"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
(action)="field.editingName = false; field.temporaryName = field.name"
|
||||
icon="red:close"
|
||||
tooltip="file-attributes-csv-import.action.cancel-edit-name"
|
||||
type="dark-bg"
|
||||
icon="red:close"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</ng-container>
|
||||
@ -146,9 +146,9 @@
|
||||
<redaction-circle-button
|
||||
(action)="field.primaryAttribute = false; toggleFieldActive.emit(field)"
|
||||
[removeTooltip]="true"
|
||||
icon="red:trash"
|
||||
tooltip="file-attributes-csv-import.action.remove"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { BaseListingComponent } from '../../../../shared/base/base-listing.component';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
import { Field } from '../file-attributes-csv-import-dialog.component';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
|
||||
@ -9,12 +9,12 @@ import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
styleUrls: ['./active-fields-listing.component.scss']
|
||||
})
|
||||
export class ActiveFieldsListingComponent extends BaseListingComponent<Field> implements OnChanges {
|
||||
@Input() public allEntities: Field[];
|
||||
@Output() public allEntitiesChange = new EventEmitter<Field[]>();
|
||||
@Output() public setHoveredColumn = new EventEmitter<string>();
|
||||
@Output() public toggleFieldActive = new EventEmitter<Field>();
|
||||
@Input() allEntities: Field[];
|
||||
@Output() allEntitiesChange = new EventEmitter<Field[]>();
|
||||
@Output() setHoveredColumn = new EventEmitter<string>();
|
||||
@Output() toggleFieldActive = new EventEmitter<Field>();
|
||||
|
||||
public readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
||||
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
||||
|
||||
protected readonly _selectionKey = 'csvColumn';
|
||||
|
||||
@ -29,20 +29,20 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
|
||||
}
|
||||
}
|
||||
|
||||
public deactivateSelection() {
|
||||
deactivateSelection() {
|
||||
this.allEntities.filter((field) => this.isEntitySelected(field)).forEach((field) => (field.primaryAttribute = false));
|
||||
this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))];
|
||||
this.allEntitiesChange.emit(this.allEntities);
|
||||
this.selectedEntitiesIds = [];
|
||||
}
|
||||
|
||||
public setAttributeForSelection(attribute: string, value: any) {
|
||||
setAttributeForSelection(attribute: string, value: any) {
|
||||
for (const csvColumn of this.selectedEntitiesIds) {
|
||||
this.allEntities.find((f) => f.csvColumn === csvColumn)[attribute] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public togglePrimary(field: Field) {
|
||||
togglePrimary(field: Field) {
|
||||
if (field.primaryAttribute) {
|
||||
field.primaryAttribute = false;
|
||||
} else {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<section class="dialog">
|
||||
<div translate="file-attributes-csv-import.title" class="dialog-header heading-l"></div>
|
||||
<div class="dialog-header heading-l" translate="file-attributes-csv-import.title"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="sub-header">
|
||||
@ -10,18 +10,18 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<form [formGroup]="baseConfigForm" (submit)="changedParseConfig && readFile()">
|
||||
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
|
||||
<div class="red-input-group required w-250">
|
||||
<mat-form-field floatLabel="always">
|
||||
<mat-label>{{ 'file-attributes-csv-import.key-column' | translate }}</mat-label>
|
||||
<input
|
||||
type="text"
|
||||
[placeholder]="'file-attributes-csv-import.key-column-placeholder' | translate"
|
||||
matInput
|
||||
formControlName="filenameMappingColumnHeaderName"
|
||||
[matAutocomplete]="auto"
|
||||
[placeholder]="'file-attributes-csv-import.key-column-placeholder' | translate"
|
||||
formControlName="filenameMappingColumnHeaderName"
|
||||
matInput
|
||||
type="text"
|
||||
/>
|
||||
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete">
|
||||
<mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
|
||||
<mat-option *ngFor="let field of filteredKeyOptions | async" [value]="field">
|
||||
{{ field }}
|
||||
</mat-option>
|
||||
@ -32,25 +32,25 @@
|
||||
<div class="red-input-group required w-110">
|
||||
<label translate="file-attributes-csv-import.delimiter"></label>
|
||||
<input
|
||||
[placeholder]="'file-attributes-csv-import.delimiter-placeholder' | translate"
|
||||
formControlName="delimiter"
|
||||
name="delimiter"
|
||||
type="text"
|
||||
[placeholder]="'file-attributes-csv-import.delimiter-placeholder' | translate"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-160">
|
||||
<label translate="file-attributes-csv-import.encoding"></label>
|
||||
<input
|
||||
[placeholder]="'file-attributes-csv-import.encoding-placeholder' | translate"
|
||||
formControlName="encoding"
|
||||
name="encoding"
|
||||
type="text"
|
||||
[placeholder]="'file-attributes-csv-import.encoding-placeholder' | translate"
|
||||
/>
|
||||
</div>
|
||||
<redaction-circle-button
|
||||
*ngIf="changedParseConfig"
|
||||
(action)="readFile()"
|
||||
*ngIf="changedParseConfig"
|
||||
icon="red:check"
|
||||
tooltip="file-attributes-csv-import.parse-csv"
|
||||
></redaction-circle-button>
|
||||
@ -68,40 +68,40 @@
|
||||
</div>
|
||||
<div class="actions">
|
||||
<redaction-circle-button
|
||||
icon="red:search"
|
||||
(click)="isSearchOpen = !isSearchOpen"
|
||||
[attr.aria-expanded]="isSearchOpen"
|
||||
icon="red:search"
|
||||
></redaction-circle-button>
|
||||
<div class="quick-activation">
|
||||
<span
|
||||
class="all-caps-label primary pointer"
|
||||
(click)="activateAll()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="file-attributes-csv-import.quick-activation.all"
|
||||
></span>
|
||||
<span
|
||||
class="all-caps-label primary pointer"
|
||||
(click)="deactivateAll()"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="file-attributes-csv-import.quick-activation.none"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-input-container" *ngIf="isSearchOpen">
|
||||
<div *ngIf="isSearchOpen" class="search-input-container">
|
||||
<redaction-search-input
|
||||
[form]="searchForm"
|
||||
placeholder="file-attributes-csv-import.search.placeholder"
|
||||
width="full"
|
||||
></redaction-search-input>
|
||||
</div>
|
||||
<div class="csv-header-pill-content" [class.search-open]="isSearchOpen">
|
||||
<div [class.search-open]="isSearchOpen" class="csv-header-pill-content">
|
||||
<div
|
||||
class="csv-header-pill-wrapper"
|
||||
*ngFor="let field of displayedEntities"
|
||||
(click)="toggleFieldActive(field)"
|
||||
(mouseenter)="setHoveredColumn(field.csvColumn)"
|
||||
(mouseleave)="setHoveredColumn()"
|
||||
(click)="toggleFieldActive(field)"
|
||||
*ngFor="let field of displayedEntities"
|
||||
class="csv-header-pill-wrapper"
|
||||
>
|
||||
<div class="csv-header-pill" [class.selected]="isActive(field)">
|
||||
<div [class.selected]="isActive(field)" class="csv-header-pill">
|
||||
<div class="name">
|
||||
{{ field.csvColumn }}
|
||||
</div>
|
||||
@ -113,19 +113,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="center" [class.collapsed]="!previewExpanded" (mouseenter)="keepPreview = true" (mouseleave)="keepPreview = false; setHoveredColumn()">
|
||||
<div (mouseenter)="keepPreview = true" (mouseleave)="keepPreview = false; setHoveredColumn()" [class.collapsed]="!previewExpanded" class="center">
|
||||
<div class="csv-part-header">
|
||||
<div class="all-caps-label">
|
||||
{{ 'file-attributes-csv-import.csv-column' + (previewExpanded ? '' : '-preview') | translate }}
|
||||
</div>
|
||||
<redaction-circle-button
|
||||
[icon]="previewExpanded ? 'red:expand' : 'red:collapse'"
|
||||
(click)="previewExpanded = !previewExpanded"
|
||||
[icon]="previewExpanded ? 'red:expand' : 'red:collapse'"
|
||||
></redaction-circle-button>
|
||||
</div>
|
||||
<div class="csv-part-content" [class.hidden]="!previewExpanded">
|
||||
<div class="no-column-data" *ngIf="!hoveredColumn" translate="file-attributes-csv-import.no-hovered-column"></div>
|
||||
<div class="no-column-data" *ngIf="hoveredColumn && !columnSample.length">
|
||||
<div [class.hidden]="!previewExpanded" class="csv-part-content">
|
||||
<div *ngIf="!hoveredColumn" class="no-column-data" translate="file-attributes-csv-import.no-hovered-column"></div>
|
||||
<div *ngIf="hoveredColumn && !columnSample.length" class="no-column-data">
|
||||
{{ 'file-attributes-csv-import.no-sample-data-for' | translate: { column: hoveredColumn } }}
|
||||
</div>
|
||||
<div *ngFor="let row of columnSample">
|
||||
@ -135,21 +135,21 @@
|
||||
</div>
|
||||
<div class="content-container">
|
||||
<redaction-active-fields-listing
|
||||
[(allEntities)]="activeFields"
|
||||
(setHoveredColumn)="setHoveredColumn($event)"
|
||||
(toggleFieldActive)="toggleFieldActive($event)"
|
||||
[(allEntities)]="activeFields"
|
||||
></redaction-active-fields-listing>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button (click)="save()" [disabled]="changedParseConfig || baseConfigForm.invalid">
|
||||
<button (click)="save()" [disabled]="changedParseConfig || baseConfigForm.invalid" color="primary" mat-flat-button>
|
||||
{{ 'file-attributes-csv-import.save.label' | translate }}
|
||||
</button>
|
||||
|
||||
<div class="all-caps-label cancel" (click)="dialogRef.close()">{{ 'file-attributes-csv-import.cancel' | translate }}</div>
|
||||
<div (click)="dialogRef.close()" class="all-caps-label cancel">{{ 'file-attributes-csv-import.cancel' | translate }}</div>
|
||||
</div>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { Component, Inject, Injector, ViewChild } from '@angular/core';
|
||||
import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as Papa from 'papaparse';
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
export interface Field {
|
||||
@ -28,22 +28,20 @@ export interface Field {
|
||||
styleUrls: ['./file-attributes-csv-import-dialog.component.scss']
|
||||
})
|
||||
export class FileAttributesCsvImportDialogComponent extends BaseListingComponent<Field> {
|
||||
protected readonly _searchKey = 'csvColumn';
|
||||
|
||||
public csvFile: File;
|
||||
public ruleSetId: string;
|
||||
public parseResult: { data: any[]; errors: any[]; meta: any; fields: Field[] };
|
||||
public hoveredColumn: string;
|
||||
public activeFields: Field[] = [];
|
||||
public baseConfigForm: FormGroup;
|
||||
public isSearchOpen = false;
|
||||
public previewExpanded = true;
|
||||
public filteredKeyOptions: Observable<string[]>;
|
||||
public keepPreview = false;
|
||||
public columnSample = [];
|
||||
public initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||
|
||||
csvFile: File;
|
||||
ruleSetId: string;
|
||||
parseResult: { data: any[]; errors: any[]; meta: any; fields: Field[] };
|
||||
hoveredColumn: string;
|
||||
activeFields: Field[] = [];
|
||||
baseConfigForm: FormGroup;
|
||||
isSearchOpen = false;
|
||||
previewExpanded = true;
|
||||
filteredKeyOptions: Observable<string[]>;
|
||||
keepPreview = false;
|
||||
columnSample = [];
|
||||
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false }) cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||
protected readonly _searchKey = 'csvColumn';
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -67,16 +65,14 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
this.readFile();
|
||||
}
|
||||
|
||||
private _autocompleteStringValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||
if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) {
|
||||
return null; /* valid option selected */
|
||||
}
|
||||
return { invalidAutocompleteString: { value: control.value } };
|
||||
};
|
||||
get changedParseConfig(): boolean {
|
||||
return (
|
||||
this.initialParseConfig.delimiter !== this.baseConfigForm.get('delimiter').value ||
|
||||
this.initialParseConfig.encoding !== this.baseConfigForm.get('encoding').value
|
||||
);
|
||||
}
|
||||
|
||||
public readFile() {
|
||||
readFile() {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', async (event) => {
|
||||
const parsedCsv = <any>event.target.result;
|
||||
@ -97,7 +93,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
|
||||
for (const entity of this.allEntities) {
|
||||
const existing = this.data.existingConfiguration.fileAttributeConfigs.find((a) => a.csvColumnHeader === entity.csvColumn);
|
||||
if (!!existing) {
|
||||
if (existing) {
|
||||
entity.id = existing.id;
|
||||
entity.name = existing.label;
|
||||
entity.temporaryName = existing.label;
|
||||
@ -130,11 +126,11 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
reader.readAsText(this.csvFile, this.baseConfigForm.get('encoding').value);
|
||||
}
|
||||
|
||||
public getSample(csvColumn: string) {
|
||||
getSample(csvColumn: string) {
|
||||
return this.parseResult?.data?.length ? this.parseResult?.data[0][csvColumn] : '';
|
||||
}
|
||||
|
||||
public getEntries(csvColumn: string) {
|
||||
getEntries(csvColumn: string) {
|
||||
if (this.parseResult?.data) {
|
||||
let count = 0;
|
||||
for (const entry of this.parseResult.data) {
|
||||
@ -148,11 +144,11 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
}
|
||||
}
|
||||
|
||||
public isActive(field: Field): boolean {
|
||||
isActive(field: Field): boolean {
|
||||
return this.activeFields.indexOf(field) !== -1;
|
||||
}
|
||||
|
||||
public toggleFieldActive(field: Field) {
|
||||
toggleFieldActive(field: Field) {
|
||||
if (!this.isActive(field)) {
|
||||
this.activeFields = [...this.activeFields, field];
|
||||
} else {
|
||||
@ -161,28 +157,15 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
}
|
||||
}
|
||||
|
||||
private _buildAttribute(csvColumn: string): Field {
|
||||
const sample = this.getSample(csvColumn);
|
||||
const isNumber = sample && !isNaN(sample);
|
||||
return {
|
||||
csvColumn,
|
||||
name: csvColumn,
|
||||
temporaryName: csvColumn,
|
||||
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
|
||||
readonly: false,
|
||||
primaryAttribute: false
|
||||
};
|
||||
}
|
||||
|
||||
public activateAll() {
|
||||
activateAll() {
|
||||
this.activeFields = [...this.allEntities];
|
||||
}
|
||||
|
||||
public deactivateAll() {
|
||||
deactivateAll() {
|
||||
this.activeFields = [];
|
||||
}
|
||||
|
||||
public async save() {
|
||||
async save() {
|
||||
const newPrimary = !!this.activeFields.find((attr) => attr.primaryAttribute);
|
||||
|
||||
if (newPrimary) {
|
||||
@ -195,16 +178,14 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
...this.data.existingConfiguration.fileAttributeConfigs.filter(
|
||||
(a) => !this.allEntities.find((entity) => entity.csvColumn === a.csvColumnHeader)
|
||||
),
|
||||
...this.activeFields.map((field) => {
|
||||
return {
|
||||
id: field.id,
|
||||
csvColumnHeader: field.csvColumn,
|
||||
editable: !field.readonly,
|
||||
label: field.name,
|
||||
type: field.type,
|
||||
primaryAttribute: field.primaryAttribute
|
||||
};
|
||||
})
|
||||
...this.activeFields.map((field) => ({
|
||||
id: field.id,
|
||||
csvColumnHeader: field.csvColumn,
|
||||
editable: !field.readonly,
|
||||
label: field.name,
|
||||
type: field.type,
|
||||
primaryAttribute: field.primaryAttribute
|
||||
}))
|
||||
]
|
||||
};
|
||||
|
||||
@ -226,7 +207,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
public setHoveredColumn(column?: string) {
|
||||
setHoveredColumn(column?: string) {
|
||||
setTimeout(() => {
|
||||
if (this.keepPreview && !column) {
|
||||
return;
|
||||
@ -241,10 +222,25 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public get changedParseConfig(): boolean {
|
||||
return (
|
||||
this.initialParseConfig.delimiter !== this.baseConfigForm.get('delimiter').value ||
|
||||
this.initialParseConfig.encoding !== this.baseConfigForm.get('encoding').value
|
||||
);
|
||||
private _autocompleteStringValidator(): ValidatorFn {
|
||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||
if ((this.parseResult?.meta?.fields || []).indexOf(control.value) !== -1) {
|
||||
return null; /* valid option selected */
|
||||
}
|
||||
return { invalidAutocompleteString: { value: control.value } };
|
||||
};
|
||||
}
|
||||
|
||||
private _buildAttribute(csvColumn: string): Field {
|
||||
const sample = this.getSample(csvColumn);
|
||||
const isNumber = sample && !isNaN(sample);
|
||||
return {
|
||||
csvColumn,
|
||||
name: csvColumn,
|
||||
temporaryName: csvColumn,
|
||||
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
|
||||
readonly: false,
|
||||
primaryAttribute: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="smtp-auth-config.form.username"></label>
|
||||
<input formControlName="user" name="user" type="text" placeholder="{{ 'smtp-auth-config.form.username-placeholder' | translate }}" />
|
||||
<input formControlName="user" name="user" placeholder="{{ 'smtp-auth-config.form.username-placeholder' | translate }}" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-300">
|
||||
@ -22,5 +22,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<redaction-circle-button icon="red:close" mat-dialog-close class="dialog-close"></redaction-circle-button>
|
||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
||||
</section>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { SMTPConfigurationModel } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
@ -9,8 +9,8 @@ import { SMTPConfigurationModel } from '@redaction/red-ui-http';
|
||||
templateUrl: './smtp-auth-dialog.component.html',
|
||||
styleUrls: ['./smtp-auth-dialog.component.scss']
|
||||
})
|
||||
export class SmtpAuthDialogComponent implements OnInit {
|
||||
public authForm: FormGroup;
|
||||
export class SmtpAuthDialogComponent {
|
||||
authForm: FormGroup;
|
||||
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
@ -24,9 +24,7 @@ export class SmtpAuthDialogComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public save() {
|
||||
save() {
|
||||
this.dialogRef.close(this.authForm.getRawValue());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AuditControllerService, AuditResponse, AuditSearchRequest } from '@redaction/red-ui-http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Moment } from 'moment';
|
||||
import { applyIntervalConstraints } from '../../../../utils/date-inputs-utils';
|
||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
|
||||
@ -14,21 +14,21 @@ const PAGE_SIZE = 50;
|
||||
styleUrls: ['./audit-screen.component.scss']
|
||||
})
|
||||
export class AuditScreenComponent {
|
||||
public filterForm: FormGroup;
|
||||
public viewReady = false;
|
||||
public categories: string[] = [];
|
||||
public userIds: Set<string>;
|
||||
public logs: AuditResponse;
|
||||
public currentPage = 1;
|
||||
readonly ALL_CATEGORIES = 'audit-screen.all-categories';
|
||||
readonly ALL_USERS = 'audit-screen.all-users';
|
||||
|
||||
public ALL_CATEGORIES = 'audit-screen.all-categories';
|
||||
public ALL_USERS = 'audit-screen.all-users';
|
||||
filterForm: FormGroup;
|
||||
viewReady = false;
|
||||
categories: string[] = [];
|
||||
userIds: Set<string>;
|
||||
logs: AuditResponse;
|
||||
currentPage = 1;
|
||||
|
||||
private _previousFrom: Moment;
|
||||
private _previousTo: Moment;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _auditControllerService: AuditControllerService,
|
||||
private readonly _translateService: TranslateService
|
||||
@ -49,6 +49,17 @@ export class AuditScreenComponent {
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
get totalPages(): number {
|
||||
if (!this.logs) {
|
||||
return 0;
|
||||
}
|
||||
return Math.ceil(this.logs.totalHits / PAGE_SIZE);
|
||||
}
|
||||
|
||||
pageChanged(page: number) {
|
||||
this._fetchData(page);
|
||||
}
|
||||
|
||||
private _updateDateFilters(value): boolean {
|
||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) {
|
||||
return true;
|
||||
@ -66,7 +77,7 @@ export class AuditScreenComponent {
|
||||
const userId = this.filterForm.get('userId').value;
|
||||
const from = this.filterForm.get('from').value;
|
||||
let to = this.filterForm.get('to').value;
|
||||
if (!!to) {
|
||||
if (to) {
|
||||
to = to.clone().add(1, 'd');
|
||||
}
|
||||
const logsRequestBody: AuditSearchRequest = {
|
||||
@ -93,15 +104,4 @@ export class AuditScreenComponent {
|
||||
this.viewReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
public get totalPages(): number {
|
||||
if (!this.logs) {
|
||||
return 0;
|
||||
}
|
||||
return Math.ceil(this.logs.totalHits / PAGE_SIZE);
|
||||
}
|
||||
|
||||
public pageChanged(page: number) {
|
||||
this._fetchData(page);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="flex-1 actions">
|
||||
<redaction-rule-set-actions></redaction-rule-set-actions>
|
||||
|
||||
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="below" icon="red:close"></redaction-circle-button>
|
||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,14 +23,14 @@
|
||||
|
||||
<div class="table-header" redactionSyncWidth="table-item">
|
||||
<redaction-table-col-name
|
||||
label="default-colors-screen.table-col-names.key"
|
||||
column="key"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
column="key"
|
||||
label="default-colors-screen.table-col-names.key"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="default-colors-screen.table-col-names.color" class="flex-center"></redaction-table-col-name>
|
||||
<redaction-table-col-name class="flex-center" label="default-colors-screen.table-col-names.color"></redaction-table-col-name>
|
||||
|
||||
<div></div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
@ -38,13 +38,13 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<!-- Table lines -->
|
||||
<div class="table-item" *cdkVirtualFor="let color of allEntities | sortBy: sortingOption.order:sortingOption.column">
|
||||
<div *cdkVirtualFor="let color of allEntities | sortBy: sortingOption.order:sortingOption.column" class="table-item">
|
||||
<div>
|
||||
<div class="table-item-title heading" [translate]="'default-colors-screen.types.' + color.key"></div>
|
||||
<div [translate]="'default-colors-screen.types.' + color.key" class="table-item-title heading"></div>
|
||||
</div>
|
||||
|
||||
<div class="color-wrapper">
|
||||
<div class="color-square" [ngStyle]="{ 'background-color': color.value }"></div>
|
||||
<div [ngStyle]="{ 'background-color': color.value }" class="color-square"></div>
|
||||
</div>
|
||||
|
||||
<div class="actions-container">
|
||||
@ -52,9 +52,9 @@
|
||||
<redaction-circle-button
|
||||
(action)="openEditColorDialog($event, color)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:edit"
|
||||
tooltip="default-colors-screen.action.edit"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Colors, DictionaryControllerService } from '@redaction/red-ui-http';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-default-colors-screen',
|
||||
@ -12,9 +12,8 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
|
||||
styleUrls: ['./default-colors-screen.component.scss']
|
||||
})
|
||||
export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: string; value: string }> {
|
||||
viewReady = false;
|
||||
protected readonly _sortKey = 'default-colors';
|
||||
|
||||
public viewReady = false;
|
||||
private _colorsObj: Colors;
|
||||
|
||||
constructor(
|
||||
@ -22,7 +21,7 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
protected readonly _injector: Injector
|
||||
) {
|
||||
super(_injector);
|
||||
@ -30,10 +29,17 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st
|
||||
this._loadColors();
|
||||
}
|
||||
|
||||
public async loadRuleSetsData(): Promise<void> {
|
||||
async loadRuleSetsData(): Promise<void> {
|
||||
await this._appStateService.loadAllRuleSets();
|
||||
}
|
||||
|
||||
openEditColorDialog($event: any, color: { key: string; value: string }) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => {
|
||||
this._loadColors();
|
||||
});
|
||||
}
|
||||
|
||||
private _loadColors() {
|
||||
this._dictionaryControllerService
|
||||
.getColors(this._appStateService.activeRuleSetId)
|
||||
@ -47,11 +53,4 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st
|
||||
this.viewReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
openEditColorDialog($event: any, color: { key: string; value: string }) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => {
|
||||
this._loadColors();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||
|
||||
<div class="flex-1 actions">
|
||||
<redaction-rule-set-actions> </redaction-rule-set-actions>
|
||||
<redaction-rule-set-actions></redaction-rule-set-actions>
|
||||
|
||||
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="below" icon="red:close"></redaction-circle-button>
|
||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -32,9 +32,9 @@
|
||||
<redaction-search-input [form]="searchForm" [placeholder]="'dictionary-listing.search'"></redaction-search-input>
|
||||
<div class="actions">
|
||||
<redaction-icon-button
|
||||
(action)="openAddEditDictionaryDialog()"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:plus"
|
||||
(action)="openAddEditDictionaryDialog()"
|
||||
text="dictionary-listing.add-new"
|
||||
type="primary"
|
||||
></redaction-icon-button>
|
||||
@ -42,36 +42,36 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-header" redactionSyncWidth="table-item" [class.no-data]="!allEntities.length">
|
||||
<div [class.no-data]="!allEntities.length" class="table-header" redactionSyncWidth="table-item">
|
||||
<div class="select-oval-placeholder"></div>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="dictionary-listing.table-col-names.type"
|
||||
column="label"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
column="label"
|
||||
label="dictionary-listing.table-col-names.type"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="dictionary-listing.table-col-names.order-of-importance"
|
||||
column="rank"
|
||||
class="flex-center"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
class="flex-center"
|
||||
column="rank"
|
||||
label="dictionary-listing.table-col-names.order-of-importance"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="dictionary-listing.table-col-names.hint-redaction" class="flex-center"></redaction-table-col-name>
|
||||
<redaction-table-col-name class="flex-center" label="dictionary-listing.table-col-names.hint-redaction"></redaction-table-col-name>
|
||||
<div></div>
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<redaction-empty-state
|
||||
*ngIf="!allEntities.length"
|
||||
icon="red:dictionary"
|
||||
(action)="openAddEditDictionaryDialog()"
|
||||
*ngIf="!allEntities.length"
|
||||
[showButton]="permissionsService.isAdmin()"
|
||||
icon="red:dictionary"
|
||||
screen="dictionary-listing"
|
||||
></redaction-empty-state>
|
||||
|
||||
@ -79,16 +79,16 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<div
|
||||
class="table-item pointer"
|
||||
*cdkVirtualFor="let dict of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
|
||||
[routerLink]="[dict.type]"
|
||||
class="table-item pointer"
|
||||
>
|
||||
<div class="selection-column" (click)="toggleEntitySelected($event, dict)">
|
||||
<div (click)="toggleEntitySelected($event, dict)" class="selection-column">
|
||||
<redaction-round-checkbox [active]="isEntitySelected(dict)"></redaction-round-checkbox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-square" [ngStyle]="{ 'background-color': dict.hexColor }"></div>
|
||||
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
|
||||
<div class="dict-name">
|
||||
<div class="table-item-title heading">
|
||||
{{ dict.label }}
|
||||
@ -119,18 +119,18 @@
|
||||
<redaction-circle-button
|
||||
(action)="openDeleteDictionaryDialog($event, dict)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:trash"
|
||||
tooltip="dictionary-listing.action.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="openAddEditDictionaryDialog($event, dict)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:edit"
|
||||
tooltip="dictionary-listing.action.edit"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
@ -144,11 +144,11 @@
|
||||
<redaction-simple-doughnut-chart
|
||||
*ngIf="allEntities.length"
|
||||
[config]="chartData"
|
||||
[strokeWidth]="15"
|
||||
[counterText]="'dictionary-listing.stats.charts.entries' | translate"
|
||||
[radius]="82"
|
||||
[strokeWidth]="15"
|
||||
[subtitle]="'dictionary-listing.stats.charts.types'"
|
||||
totalType="count"
|
||||
[counterText]="'dictionary-listing.stats.charts.entries' | translate"
|
||||
></redaction-simple-doughnut-chart>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { Component, Injector, OnInit } from '@angular/core';
|
||||
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { defaultIfEmpty, tap } from 'rxjs/operators';
|
||||
import { forkJoin } from 'rxjs';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dictionary-listing-screen',
|
||||
@ -15,19 +15,18 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
|
||||
styleUrls: ['./dictionary-listing-screen.component.scss']
|
||||
})
|
||||
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValue> implements OnInit {
|
||||
viewReady = false;
|
||||
chartData: DoughnutChartConfig[] = [];
|
||||
protected readonly _searchKey = 'label';
|
||||
protected readonly _selectionKey = 'type';
|
||||
protected readonly _sortKey = 'dictionary-listing';
|
||||
|
||||
public viewReady = false;
|
||||
public chartData: DoughnutChartConfig[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
protected readonly _injector: Injector
|
||||
) {
|
||||
super(_injector);
|
||||
@ -38,6 +37,23 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
|
||||
this._loadDictionaryData();
|
||||
}
|
||||
|
||||
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
|
||||
$event?.stopPropagation();
|
||||
this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
|
||||
if (newDictionary) {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
|
||||
this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
});
|
||||
}
|
||||
|
||||
private _loadDictionaryData() {
|
||||
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
|
||||
this.allEntities = Object.keys(appStateDictionaryData)
|
||||
@ -71,21 +87,4 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
|
||||
this.chartData.sort((a, b) => (a.label < b.label ? -1 : 1));
|
||||
this.viewReady = true;
|
||||
}
|
||||
|
||||
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
|
||||
$event?.stopPropagation();
|
||||
this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
|
||||
if (newDictionary) {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
|
||||
this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,46 +6,46 @@
|
||||
<redaction-circle-button
|
||||
(action)="openDeleteDictionaryDialog($event)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:trash"
|
||||
tooltip="dictionary-overview.action.delete"
|
||||
tooltipPosition="below"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="openEditDictionaryDialog($event)"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:edit"
|
||||
tooltip="dictionary-overview.action.edit"
|
||||
tooltipPosition="below"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="download()"
|
||||
icon="red:download"
|
||||
tooltip="dictionary-overview.action.download"
|
||||
tooltipPosition="below"
|
||||
icon="red:download"
|
||||
></redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
(action)="fileInput.click()"
|
||||
*ngIf="permissionsService.isAdmin()"
|
||||
icon="red:upload"
|
||||
tooltip="dictionary-overview.action.upload"
|
||||
tooltipPosition="below"
|
||||
icon="red:upload"
|
||||
></redaction-circle-button>
|
||||
|
||||
<input #fileInput (change)="upload($event)" hidden class="file-upload-input" type="file" accept="text/plain" />
|
||||
<input #fileInput (change)="upload($event)" accept="text/plain" class="file-upload-input" hidden type="file" />
|
||||
|
||||
<redaction-circle-button
|
||||
class="ml-6"
|
||||
[routerLink]="['..']"
|
||||
class="ml-6"
|
||||
icon="red:close"
|
||||
tooltip="common.close"
|
||||
tooltipPosition="below"
|
||||
icon="red:close"
|
||||
></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
@ -59,43 +59,43 @@
|
||||
<div class="actions-bar">
|
||||
<div class="red-input-group w-450 mr-32">
|
||||
<input
|
||||
[class.with-matches]="searchText.length > 0"
|
||||
type="text"
|
||||
[(ngModel)]="searchText"
|
||||
(keyup)="searchChanged(searchText)"
|
||||
#inputElement
|
||||
(keyup)="searchChanged(searchText)"
|
||||
[(ngModel)]="searchText"
|
||||
[class.with-matches]="searchText.length > 0"
|
||||
placeholder="{{ 'dictionary-overview.search' | translate }}"
|
||||
type="text"
|
||||
/>
|
||||
|
||||
<div class="input-icons">
|
||||
<div class="no-input" *ngIf="searchText.length === 0">
|
||||
<div *ngIf="searchText.length === 0" class="no-input">
|
||||
<mat-icon svgIcon="red:search"></mat-icon>
|
||||
</div>
|
||||
<div class="with-input" *ngIf="searchText.length > 0">
|
||||
<div *ngIf="searchText.length > 0" class="with-input">
|
||||
<div class="search-match-text">
|
||||
{{ currentMatch + '/' + searchPositions.length }}
|
||||
</div>
|
||||
<mat-icon svgIcon="red:arrow-up" class="pointer" (click)="previousSearchMatch()"></mat-icon>
|
||||
<mat-icon svgIcon="red:arrow-down" class="pointer" (click)="nextSearchMatch()"></mat-icon>
|
||||
<mat-icon svgIcon="red:close" (click)="searchChanged(''); inputElement.focus()" class="pointer"></mat-icon>
|
||||
<mat-icon (click)="previousSearchMatch()" class="pointer" svgIcon="red:arrow-up"></mat-icon>
|
||||
<mat-icon (click)="nextSearchMatch()" class="pointer" svgIcon="red:arrow-down"></mat-icon>
|
||||
<mat-icon (click)="searchChanged(''); inputElement.focus()" class="pointer" svgIcon="red:close"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form class="compare-form" [formGroup]="compareForm">
|
||||
<form [formGroup]="compareForm" class="compare-form">
|
||||
<div class="red-input-group mr-16">
|
||||
<mat-checkbox formControlName="active" color="primary"> {{ 'dictionary-overview.compare.compare' | translate }} </mat-checkbox>
|
||||
<mat-checkbox color="primary" formControlName="active"> {{ 'dictionary-overview.compare.compare' | translate }} </mat-checkbox>
|
||||
</div>
|
||||
<div class="red-input-group w-200 mr-8">
|
||||
<mat-select formControlName="ruleSet">
|
||||
<mat-option *ngFor="let ruleSet of ruleSets" [value]="ruleSet">
|
||||
{{ ruleSet === SELECT_RULESET ? (ruleSet.name | translate) : ruleSet.name }}
|
||||
{{ ruleSet === selectRuleSet ? (ruleSet.name | translate) : ruleSet.name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
<div class="red-input-group w-200">
|
||||
<mat-select formControlName="dictionary">
|
||||
<mat-option *ngFor="let dictionary of dictionaries" [value]="dictionary">
|
||||
{{ dictionary === SELECT_DICTIONARY ? (dictionary.label | translate) : dictionary.label }}
|
||||
{{ dictionary === selectDictionary ? (dictionary.label | translate) : dictionary.label }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</div>
|
||||
@ -105,40 +105,40 @@
|
||||
<div class="editor-container">
|
||||
<ace-editor
|
||||
#editorComponent
|
||||
[mode]="'text'"
|
||||
[theme]="'eclipse'"
|
||||
[options]="aceOptions"
|
||||
[readOnly]="!permissionsService.isAdmin()"
|
||||
(textChanged)="textChanged($event)"
|
||||
[autoUpdateContent]="true"
|
||||
[mode]="'text'"
|
||||
[options]="aceOptions"
|
||||
[readOnly]="!permissionsService.isAdmin()"
|
||||
[theme]="'eclipse'"
|
||||
class="ace-redaction"
|
||||
>
|
||||
</ace-editor>
|
||||
<div class="no-dictionary-selected" *ngIf="compareForm.get('active').value && compareForm.get('dictionary').value === SELECT_DICTIONARY">
|
||||
<div *ngIf="compareForm.get('active').value && compareForm.get('dictionary').value === selectDictionary" class="no-dictionary-selected">
|
||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||
<span class="heading-l" translate="dictionary-overview.select-dictionary"></span>
|
||||
</div>
|
||||
<ace-editor
|
||||
#compareEditorComponent
|
||||
*ngIf="compareForm.get('active').value && compareForm.get('dictionary').value !== SELECT_DICTIONARY"
|
||||
*ngIf="compareForm.get('active').value && compareForm.get('dictionary').value !== selectDictionary"
|
||||
[mode]="'text'"
|
||||
[theme]="'eclipse'"
|
||||
[options]="aceOptions"
|
||||
[readOnly]="true"
|
||||
[theme]="'eclipse'"
|
||||
class="ace-redaction"
|
||||
>
|
||||
</ace-editor>
|
||||
</div>
|
||||
|
||||
<div class="changes-box" *ngIf="hasChanges && permissionsService.isAdmin()" [class.offset]="compareForm.get('active').value">
|
||||
<redaction-icon-button icon="red:check" (action)="saveEntries()" text="dictionary-overview.save-changes" type="primary"></redaction-icon-button>
|
||||
<div class="all-caps-label cancel" (click)="revert()" translate="dictionary-overview.revert-changes"></div>
|
||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" [class.offset]="compareForm.get('active').value" class="changes-box">
|
||||
<redaction-icon-button (action)="saveEntries()" icon="red:check" text="dictionary-overview.save-changes" type="primary"></redaction-icon-button>
|
||||
<div (click)="revert()" class="all-caps-label cancel" translate="dictionary-overview.revert-changes"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-container">
|
||||
<div class="dictionary-header">
|
||||
<div class="color-box" [style.backgroundColor]="dictionary.hexColor"></div>
|
||||
<div [style.backgroundColor]="dictionary.hexColor" class="color-box"></div>
|
||||
<div class="heading-xl">
|
||||
{{ dictionary.type | humanize }}
|
||||
</div>
|
||||
@ -166,7 +166,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pb-32 mt-20" *ngIf="!!dictionary.description">
|
||||
<div *ngIf="!!dictionary.description" class="pb-32 mt-20">
|
||||
<div class="heading" translate="dictionary-overview.dictionary-details.description"></div>
|
||||
<div class="mt-8">{{ dictionary.description }}</div>
|
||||
</div>
|
||||
|
||||
@ -102,6 +102,7 @@
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.red-input-group {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { DictionaryControllerService, RuleSetModel, TypeValue } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AceEditorComponent } from 'ng2-ace-editor';
|
||||
import { debounce } from '../../../../utils/debounce';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { debounce } from '@utils/debounce';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '../../../../guards/can-deactivate.guard';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
|
||||
declare var ace;
|
||||
declare let ace;
|
||||
const MIN_WORD_LENGTH = 2;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-dictionary-overview-screen',
|
||||
@ -21,8 +22,6 @@ declare var ace;
|
||||
styleUrls: ['./dictionary-overview-screen.component.scss']
|
||||
})
|
||||
export class DictionaryOverviewScreenComponent extends ComponentHasChanges implements OnInit {
|
||||
static readonly MIN_WORD_LENGTH: number = 2;
|
||||
|
||||
activeEditMarkers: any[] = [];
|
||||
activeSearchMarkers: any[] = [];
|
||||
searchPositions: any[] = [];
|
||||
@ -35,18 +34,18 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
searchText = '';
|
||||
processing = true;
|
||||
|
||||
public SELECT_RULESET = { name: 'dictionary-overview.compare.select-ruleset' };
|
||||
public SELECT_DICTIONARY = { label: 'dictionary-overview.compare.select-dictionary' };
|
||||
public ruleSets: RuleSetModel[];
|
||||
public dictionaries: TypeValue[] = [this.SELECT_DICTIONARY];
|
||||
public compareForm: FormGroup;
|
||||
selectRuleSet = { name: 'dictionary-overview.compare.select-ruleset' };
|
||||
selectDictionary = { label: 'dictionary-overview.compare.select-dictionary' };
|
||||
ruleSets: RuleSetModel[];
|
||||
dictionaries: TypeValue[] = [this.selectDictionary];
|
||||
compareForm: FormGroup;
|
||||
|
||||
@ViewChild('editorComponent', { static: true }) private _editorComponent: AceEditorComponent;
|
||||
@ViewChild('compareEditorComponent') private _compareEditorComponent: AceEditorComponent;
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
protected readonly _translateService: TranslateService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
@ -61,17 +60,17 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
|
||||
this.compareForm = this._formBuilder.group({
|
||||
active: [false],
|
||||
ruleSet: [{ value: this.SELECT_RULESET, disabled: true }],
|
||||
dictionary: [{ value: this.SELECT_DICTIONARY, disabled: true }]
|
||||
ruleSet: [{ value: this.selectRuleSet, disabled: true }],
|
||||
dictionary: [{ value: this.selectDictionary, disabled: true }]
|
||||
});
|
||||
|
||||
this.compareForm.valueChanges.subscribe((value) => {
|
||||
this._setFieldStatus('ruleSet', value.active);
|
||||
this._setFieldStatus('dictionary', value.active && this.compareForm.get('ruleSet').value !== this.SELECT_RULESET);
|
||||
this._setFieldStatus('dictionary', value.active && this.compareForm.get('ruleSet').value !== this.selectRuleSet);
|
||||
this._loadDictionaries();
|
||||
});
|
||||
|
||||
this.ruleSets = [this.SELECT_RULESET, ...this._appStateService.ruleSets];
|
||||
this.ruleSets = [this.selectRuleSet, ...this._appStateService.ruleSets];
|
||||
|
||||
this._initializeEditor();
|
||||
|
||||
@ -84,11 +83,11 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
});
|
||||
}
|
||||
|
||||
public get dictionary(): TypeValue {
|
||||
get dictionary(): TypeValue {
|
||||
return this._appStateService.activeDictionary;
|
||||
}
|
||||
|
||||
public get hasChanges() {
|
||||
get hasChanges() {
|
||||
return (
|
||||
this.currentDictionaryEntries.length &&
|
||||
(this.activeEditMarkers.length > 0 ||
|
||||
@ -112,14 +111,14 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
});
|
||||
}
|
||||
|
||||
public openEditDictionaryDialog($event: any) {
|
||||
openEditDictionaryDialog($event: any) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openAddEditDictionaryDialog(this.dictionary, this.dictionary.ruleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteDictionaryDialog($event: any) {
|
||||
openDeleteDictionaryDialog($event: any) {
|
||||
this._dialogService.openDeleteDictionaryDialog($event, this.dictionary, this.dictionary.ruleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._router.navigate(['..']);
|
||||
@ -127,7 +126,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
|
||||
@debounce()
|
||||
public searchChanged(text: string) {
|
||||
searchChanged(text: string) {
|
||||
this.searchText = text.toLowerCase();
|
||||
this._applySearchMarkers();
|
||||
this.currentMatch = 0;
|
||||
@ -135,7 +134,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
|
||||
@debounce(500)
|
||||
public textChanged($event: any) {
|
||||
textChanged($event: any) {
|
||||
this._applySearchMarkers();
|
||||
this.currentDictionaryEntries = $event.split('\n');
|
||||
this.changedLines = [];
|
||||
@ -151,28 +150,28 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
}
|
||||
|
||||
const Range = ace.require('ace/range').Range;
|
||||
const range = ace.require('ace/range').Range;
|
||||
for (const i of this.changedLines) {
|
||||
const entry = this.currentDictionaryEntries[i];
|
||||
if (entry?.trim().length > 0) {
|
||||
// only mark non-empty lines
|
||||
this.activeEditMarkers.push(this._editorComponent.getEditor().getSession().addMarker(new Range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||
this.activeEditMarkers.push(this._editorComponent.getEditor().getSession().addMarker(new range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||
}
|
||||
if (entry?.trim().length > 0 && entry.trim().length < DictionaryOverviewScreenComponent.MIN_WORD_LENGTH) {
|
||||
if (entry?.trim().length > 0 && entry.trim().length < MIN_WORD_LENGTH) {
|
||||
// show lines that are too short
|
||||
this.activeEditMarkers.push(this._editorComponent.getEditor().getSession().addMarker(new Range(i, 0, i, 1), 'too-short-marker', 'fullLine'));
|
||||
this.activeEditMarkers.push(this._editorComponent.getEditor().getSession().addMarker(new range(i, 0, i, 1), 'too-short-marker', 'fullLine'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async saveEntries() {
|
||||
async saveEntries() {
|
||||
let entriesToAdd = [];
|
||||
this.currentDictionaryEntries.forEach((currentEntry) => {
|
||||
entriesToAdd.push(currentEntry);
|
||||
});
|
||||
// remove empty lines
|
||||
entriesToAdd = entriesToAdd.filter((e) => e && e.trim().length > 0).map((e) => e.trim());
|
||||
const invalidRowsExist = entriesToAdd.filter((e) => e.length < DictionaryOverviewScreenComponent.MIN_WORD_LENGTH);
|
||||
const invalidRowsExist = entriesToAdd.filter((e) => e.length < MIN_WORD_LENGTH);
|
||||
if (invalidRowsExist.length === 0) {
|
||||
// can add at least 1 - block UI
|
||||
this.processing = true;
|
||||
@ -210,13 +209,13 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
}
|
||||
|
||||
public revert() {
|
||||
revert() {
|
||||
DictionaryOverviewScreenComponent._setEditorValue(this._editorComponent, this.initialDictionaryEntries);
|
||||
this.searchChanged('');
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
public nextSearchMatch() {
|
||||
nextSearchMatch() {
|
||||
// length = 3
|
||||
if (this.searchPositions.length > 0) {
|
||||
this.currentMatch = this.currentMatch < this.searchPositions.length ? this.currentMatch + 1 : 1;
|
||||
@ -224,14 +223,14 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
}
|
||||
|
||||
public previousSearchMatch() {
|
||||
previousSearchMatch() {
|
||||
if (this.searchPositions.length > 0) {
|
||||
this.currentMatch = this.currentMatch > 1 ? this.currentMatch - 1 : this.searchPositions.length;
|
||||
this._gotoLine();
|
||||
}
|
||||
}
|
||||
|
||||
public download(): void {
|
||||
download(): void {
|
||||
const content = this._editorComponent.getEditor().getValue();
|
||||
const blob = new Blob([content], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
@ -239,7 +238,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
saveAs(blob, `${this.dictionary.label}.txt`);
|
||||
}
|
||||
|
||||
public upload($event): void {
|
||||
upload($event): void {
|
||||
const file = $event.target.files[0];
|
||||
const fileReader = new FileReader();
|
||||
|
||||
@ -253,18 +252,18 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
}
|
||||
|
||||
private _syncActiveLines() {
|
||||
if (!!this._compareEditorComponent) {
|
||||
if (this._compareEditorComponent) {
|
||||
this._compareEditorComponent.getEditor().gotoLine(this._activeRow);
|
||||
}
|
||||
}
|
||||
|
||||
private _onRuleSetChanged() {
|
||||
this._loadDictionaries();
|
||||
this.compareForm.patchValue({ dictionary: this.SELECT_DICTIONARY });
|
||||
this.compareForm.patchValue({ dictionary: this.selectDictionary });
|
||||
}
|
||||
|
||||
private _onDictionaryChanged(dictionary: TypeValue) {
|
||||
if (dictionary !== this.SELECT_DICTIONARY) {
|
||||
if (dictionary !== this.selectDictionary) {
|
||||
this._dictionaryControllerService.getDictionaryForType(dictionary.type, dictionary.ruleSetId).subscribe(
|
||||
(data) => {
|
||||
this.compareDictionaryEntries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
@ -285,12 +284,12 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
private _loadDictionaries() {
|
||||
const ruleSetId = this.compareForm.get('ruleSet').value.ruleSetId;
|
||||
if (!ruleSetId) {
|
||||
this.dictionaries = [this.SELECT_DICTIONARY];
|
||||
this.dictionaries = [this.selectDictionary];
|
||||
return;
|
||||
}
|
||||
const appStateDictionaryData = this._appStateService.dictionaryData[ruleSetId];
|
||||
this.dictionaries = [
|
||||
this.SELECT_DICTIONARY,
|
||||
this.selectDictionary,
|
||||
...Object.keys(appStateDictionaryData)
|
||||
.map((key) => appStateDictionaryData[key])
|
||||
.filter((d) => !d.virtual || d.type === 'false_positive')
|
||||
@ -317,13 +316,13 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
});
|
||||
this.activeSearchMarkers = [];
|
||||
|
||||
const Range = ace.require('ace/range').Range;
|
||||
const range = ace.require('ace/range').Range;
|
||||
for (const position of this.searchPositions) {
|
||||
this.activeSearchMarkers.push(
|
||||
this._editorComponent
|
||||
.getEditor()
|
||||
.getSession()
|
||||
.addMarker(new Range(position.row, position.column, position.row, position.column + position.length), 'search-marker', 'text')
|
||||
.addMarker(new range(position.row, position.column, position.row, position.column + position.length), 'search-marker', 'text')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DigitalSignature, DigitalSignatureControllerService } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { lastIndexOfEnd } from '../../../../utils/functions';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { lastIndexOfEnd } from '@utils/functions';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-digital-signature-screen',
|
||||
@ -12,22 +12,26 @@ import { lastIndexOfEnd } from '../../../../utils/functions';
|
||||
styleUrls: ['./digital-signature-screen.component.scss']
|
||||
})
|
||||
export class DigitalSignatureScreenComponent {
|
||||
public digitalSignature: DigitalSignature;
|
||||
public digitalSignatureForm: FormGroup;
|
||||
digitalSignature: DigitalSignature;
|
||||
digitalSignatureForm: FormGroup;
|
||||
|
||||
public viewReady = false;
|
||||
public digitalSignatureExists = false;
|
||||
viewReady = false;
|
||||
digitalSignatureExists = false;
|
||||
|
||||
constructor(
|
||||
private readonly _digitalSignatureControllerService: DigitalSignatureControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _translateService: TranslateService,
|
||||
public readonly permissionsService: PermissionsService
|
||||
readonly permissionsService: PermissionsService
|
||||
) {
|
||||
this.loadDigitalSignatureAndInitializeForm();
|
||||
}
|
||||
|
||||
get hasDigitalSignatureSet() {
|
||||
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
|
||||
}
|
||||
|
||||
saveDigitalSignature() {
|
||||
const digitalSignature = {
|
||||
...this.digitalSignatureForm.getRawValue()
|
||||
@ -119,6 +123,8 @@ export class DigitalSignatureScreenComponent {
|
||||
});
|
||||
}
|
||||
|
||||
formChanged() {}
|
||||
|
||||
private _initForm() {
|
||||
this.digitalSignatureForm = this._formBuilder.group({
|
||||
certificateName: [this.digitalSignature.certificateName, Validators.required],
|
||||
@ -129,10 +135,4 @@ export class DigitalSignatureScreenComponent {
|
||||
base64EncodedPrivateKey: this.digitalSignatureExists ? null : [this.digitalSignature.base64EncodedPrivateKey, Validators.required]
|
||||
});
|
||||
}
|
||||
|
||||
get hasDigitalSignatureSet() {
|
||||
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
|
||||
}
|
||||
|
||||
formChanged() {}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||
|
||||
<div class="actions flex-1">
|
||||
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="below" icon="red:close"></redaction-circle-button>
|
||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -27,11 +27,11 @@
|
||||
</span>
|
||||
|
||||
<redaction-circle-button
|
||||
(click)="openConfirmDeleteAttributeDialog($event)"
|
||||
*ngIf="areSomeEntitiesSelected"
|
||||
icon="red:trash"
|
||||
tooltip="file-attributes-listing.bulk-actions.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
(click)="openConfirmDeleteAttributeDialog($event)"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
@ -39,19 +39,19 @@
|
||||
|
||||
<div class="attributes-actions-container">
|
||||
<redaction-search-input [form]="searchForm" [placeholder]="'file-attributes-listing.search'"></redaction-search-input>
|
||||
<input #fileInput (change)="importCSV($event.target['files'])" class="csv-input" type="file" accept=".csv" />
|
||||
<input #fileInput (change)="importCSV($event.target['files'])" accept=".csv" class="csv-input" type="file" />
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="fileInput.click()"
|
||||
icon="red:upload"
|
||||
tooltip="file-attributes-listing.upload-csv"
|
||||
tooltipPosition="above"
|
||||
icon="red:upload"
|
||||
type="dark-bg"
|
||||
></redaction-circle-button>
|
||||
|
||||
<redaction-icon-button
|
||||
icon="red:plus"
|
||||
(action)="openAddEditAttributeDialog($event)"
|
||||
icon="red:plus"
|
||||
text="file-attributes-listing.add-new"
|
||||
type="primary"
|
||||
></redaction-icon-button>
|
||||
@ -62,37 +62,37 @@
|
||||
<div class="select-oval-placeholder"></div>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="file-attributes-listing.table-col-names.name"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
column="label"
|
||||
label="file-attributes-listing.table-col-names.name"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="file-attributes-listing.table-col-names.type"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
column="type"
|
||||
label="file-attributes-listing.table-col-names.type"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
label="file-attributes-listing.table-col-names.read-only"
|
||||
class="flex-center"
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
[withSort]="true"
|
||||
class="flex-center"
|
||||
column="editable"
|
||||
label="file-attributes-listing.table-col-names.read-only"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="file-attributes-listing.table-col-names.csv-column"></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
class="flex-center"
|
||||
label="file-attributes-listing.table-col-names.primary"
|
||||
rightIcon="red:status-info"
|
||||
rightIconTooltip="file-attributes-listing.table-col-names.primary-info-tooltip"
|
||||
class="flex-center"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<div></div>
|
||||
@ -100,7 +100,7 @@
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<redaction-empty-state *ngIf="!allEntities.length" screen="file-attributes-listing" icon="red:attribute"></redaction-empty-state>
|
||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:attribute" screen="file-attributes-listing"></redaction-empty-state>
|
||||
|
||||
<redaction-empty-state
|
||||
*ngIf="allEntities.length && !displayedEntities.length"
|
||||
@ -110,8 +110,8 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<!-- Table lines -->
|
||||
<div class="table-item" *cdkVirtualFor="let attribute of displayedEntities | sortBy: sortingOption.order:sortingOption.column">
|
||||
<div class="selection-column" (click)="toggleEntitySelected($event, attribute)">
|
||||
<div *cdkVirtualFor="let attribute of displayedEntities | sortBy: sortingOption.order:sortingOption.column" class="table-item">
|
||||
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
|
||||
<redaction-round-checkbox [active]="isEntitySelected(attribute)"></redaction-round-checkbox>
|
||||
</div>
|
||||
|
||||
@ -119,36 +119,36 @@
|
||||
<span>{{ attribute.label }}</span>
|
||||
</div>
|
||||
|
||||
<div class="small-label" [translate]="'file-attribute-types.' + attribute.type"></div>
|
||||
<div [translate]="'file-attribute-types.' + attribute.type" class="small-label"></div>
|
||||
|
||||
<div class="center read-only">
|
||||
<mat-icon
|
||||
svgIcon="red:read-only"
|
||||
*ngIf="!attribute.editable"
|
||||
[matTooltip]="'file-attributes-listing.read-only' | translate"
|
||||
matTooltipPosition="above"
|
||||
svgIcon="red:read-only"
|
||||
></mat-icon>
|
||||
</div>
|
||||
<div class="small-label">
|
||||
{{ attribute.csvColumnHeader }}
|
||||
</div>
|
||||
<div class="center">
|
||||
<redaction-round-checkbox *ngIf="attribute.primaryAttribute" [size]="18" [active]="true"></redaction-round-checkbox>
|
||||
<redaction-round-checkbox *ngIf="attribute.primaryAttribute" [active]="true" [size]="18"></redaction-round-checkbox>
|
||||
</div>
|
||||
<div class="actions-container">
|
||||
<div class="action-buttons">
|
||||
<redaction-circle-button
|
||||
(action)="openAddEditAttributeDialog($event, attribute)"
|
||||
icon="red:edit"
|
||||
tooltip="file-attributes-listing.action.edit"
|
||||
type="dark-bg"
|
||||
icon="red:edit"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
(action)="openConfirmDeleteAttributeDialog($event, attribute)"
|
||||
icon="red:trash"
|
||||
tooltip="file-attributes-listing.action.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-attributes-listing-screen',
|
||||
@ -12,19 +12,17 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
|
||||
styleUrls: ['./file-attributes-listing-screen.component.scss']
|
||||
})
|
||||
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit {
|
||||
viewReady = false;
|
||||
loading = false;
|
||||
protected readonly _searchKey = 'label';
|
||||
protected readonly _selectionKey = 'id';
|
||||
protected readonly _sortKey = 'file-attributes-listing';
|
||||
|
||||
public viewReady = false;
|
||||
public loading = false;
|
||||
|
||||
private _existingConfiguration: FileAttributesConfig;
|
||||
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _fileAttributesService: FileAttributesControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
@ -39,6 +37,37 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async (newValue: FileAttributeConfig) => {
|
||||
this.loading = true;
|
||||
await this._fileAttributesService.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId).toPromise();
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openConfirmDeleteFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
|
||||
this.loading = true;
|
||||
if (fileAttribute) {
|
||||
await this._fileAttributesService.deleteFileAttribute(this._appStateService.activeRuleSetId, fileAttribute.id).toPromise();
|
||||
} else {
|
||||
await this._fileAttributesService.deleteFileAttributes(this.selectedEntitiesIds, this._appStateService.activeRuleSetId).toPromise();
|
||||
}
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
importCSV(files: FileList | File[]) {
|
||||
const csvFile = files[0];
|
||||
this._fileInput.nativeElement.value = null;
|
||||
|
||||
this._dialogService.openImportFileAttributeCSVDialog(csvFile, this._appStateService.activeRuleSetId, this._existingConfiguration, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
|
||||
@ -51,35 +80,4 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async (newValue: FileAttributeConfig) => {
|
||||
this.loading = true;
|
||||
await this._fileAttributesService.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId).toPromise();
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
public openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openConfirmDeleteFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
|
||||
this.loading = true;
|
||||
if (!!fileAttribute) {
|
||||
await this._fileAttributesService.deleteFileAttribute(this._appStateService.activeRuleSetId, fileAttribute.id).toPromise();
|
||||
} else {
|
||||
await this._fileAttributesService.deleteFileAttributes(this.selectedEntitiesIds, this._appStateService.activeRuleSetId).toPromise();
|
||||
}
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
|
||||
public importCSV(files: FileList | File[]) {
|
||||
const csvFile = files[0];
|
||||
this._fileInput.nativeElement.value = null;
|
||||
|
||||
this._dialogService.openImportFileAttributeCSVDialog(csvFile, this._appStateService.activeRuleSetId, this._existingConfiguration, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { LicenseReport, LicenseReportControllerService } from '@redaction/red-ui-http';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { AppConfigService } from '@app-config/app-config.service';
|
||||
import * as moment from 'moment';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@ -11,9 +11,30 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
styleUrls: ['./license-information-screen.component.scss']
|
||||
})
|
||||
export class LicenseInformationScreenComponent implements OnInit {
|
||||
currentInfo: LicenseReport = {};
|
||||
totalInfo: LicenseReport = {};
|
||||
unlicensedInfo: LicenseReport = {};
|
||||
totalLicensedNumberOfPages = 0;
|
||||
analysisPercentageOfLicense = 100;
|
||||
viewReady = false;
|
||||
barChart: any[] = [];
|
||||
lineChartSeries: any[] = [];
|
||||
yAxisLabel = this._translateService.instant('license-info-screen.chart.pages-per-month');
|
||||
yAxisLabelRight = this._translateService.instant('license-info-screen.chart.total-pages');
|
||||
lineChartScheme = {
|
||||
selectable: true,
|
||||
group: 'Ordinal',
|
||||
domain: ['#dd4d50', '#5ce594', '#0389ec']
|
||||
};
|
||||
comboBarScheme = {
|
||||
selectable: true,
|
||||
group: 'Ordinal',
|
||||
domain: ['#0389ec']
|
||||
};
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly appConfigService: AppConfigService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly appConfigService: AppConfigService,
|
||||
private readonly _licenseReportController: LicenseReportControllerService,
|
||||
private readonly _translateService: TranslateService
|
||||
) {}
|
||||
@ -21,32 +42,8 @@ export class LicenseInformationScreenComponent implements OnInit {
|
||||
get currentYear(): number {
|
||||
return new Date().getFullYear();
|
||||
}
|
||||
public currentInfo: LicenseReport = {};
|
||||
public totalInfo: LicenseReport = {};
|
||||
public unlicensedInfo: LicenseReport = {};
|
||||
public totalLicensedNumberOfPages = 0;
|
||||
public analysisPercentageOfLicense = 100;
|
||||
public viewReady = false;
|
||||
|
||||
barChart: any[] = [];
|
||||
lineChartSeries: any[] = [];
|
||||
|
||||
yAxisLabel = this._translateService.instant('license-info-screen.chart.pages-per-month');
|
||||
yAxisLabelRight = this._translateService.instant('license-info-screen.chart.total-pages');
|
||||
|
||||
lineChartScheme = {
|
||||
selectable: true,
|
||||
group: 'Ordinal',
|
||||
domain: ['#dd4d50', '#5ce594', '#0389ec']
|
||||
};
|
||||
|
||||
comboBarScheme = {
|
||||
selectable: true,
|
||||
group: 'Ordinal',
|
||||
domain: ['#0389ec']
|
||||
};
|
||||
|
||||
public async ngOnInit() {
|
||||
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');
|
||||
@ -74,6 +71,16 @@ export class LicenseInformationScreenComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
sendMail(): void {
|
||||
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
|
||||
const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer });
|
||||
const body = [
|
||||
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
|
||||
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
|
||||
].join('%0D%0A');
|
||||
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
|
||||
}
|
||||
|
||||
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
|
||||
const [startMonth, startYear] = [startDate.month(), startDate.year()];
|
||||
const [endMonth, endYear] = [endDate.month(), endDate.year()];
|
||||
@ -144,14 +151,4 @@ export class LicenseInformationScreenComponent implements OnInit {
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
sendMail(): void {
|
||||
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
|
||||
const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer });
|
||||
const body = [
|
||||
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
|
||||
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
|
||||
].join('%0D%0A');
|
||||
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, Injector, OnInit } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { UserPreferenceService } from '../../../../services/user-preference.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
import { RuleSetModel } from '@redaction/red-ui-http';
|
||||
|
||||
@Component({
|
||||
@ -19,8 +19,8 @@ export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSet
|
||||
constructor(
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly userPreferenceService: UserPreferenceService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
protected readonly _injector: Injector
|
||||
) {
|
||||
super(_injector);
|
||||
@ -30,13 +30,21 @@ export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSet
|
||||
this.loadRuleSetsData();
|
||||
}
|
||||
|
||||
public loadRuleSetsData() {
|
||||
loadRuleSetsData() {
|
||||
this._appStateService.reset();
|
||||
this.allEntities = this._appStateService.ruleSets;
|
||||
this._executeSearchImmediately();
|
||||
this._loadRuleSetStats();
|
||||
}
|
||||
|
||||
openAddRuleSetDialog() {
|
||||
this._dialogService.openAddEditRuleSetDialog(null, async (newRuleSet) => {
|
||||
if (newRuleSet) {
|
||||
this.loadRuleSetsData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _loadRuleSetStats() {
|
||||
this.allEntities.forEach((rs) => {
|
||||
const dictionaries = this._appStateService.dictionaryData[rs.ruleSetId];
|
||||
@ -50,12 +58,4 @@ export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSet
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openAddRuleSetDialog() {
|
||||
this._dialogService.openAddEditRuleSetDialog(null, async (newRuleSet) => {
|
||||
if (newRuleSet) {
|
||||
this.loadRuleSetsData();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<div class="flex-1 actions">
|
||||
<redaction-rule-set-actions></redaction-rule-set-actions>
|
||||
|
||||
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="below" icon="red:close"></redaction-circle-button>
|
||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17,20 +17,20 @@
|
||||
<div class="editor-container">
|
||||
<ace-editor
|
||||
#editorComponent
|
||||
[mode]="'java'"
|
||||
[theme]="'eclipse'"
|
||||
[options]="aceOptions"
|
||||
[readOnly]="!permissionsService.isAdmin()"
|
||||
(textChanged)="textChanged($event)"
|
||||
[autoUpdateContent]="true"
|
||||
[mode]="'java'"
|
||||
[options]="aceOptions"
|
||||
[readOnly]="!permissionsService.isAdmin()"
|
||||
[text]="rules"
|
||||
[theme]="'eclipse'"
|
||||
class="ace-redaction"
|
||||
>
|
||||
</ace-editor>
|
||||
</div>
|
||||
<div class="changes-box" *ngIf="hasChanges && permissionsService.isAdmin()">
|
||||
<redaction-icon-button icon="red:check" (action)="save()" text="rules-screen.save-changes" type="primary"></redaction-icon-button>
|
||||
<div (click)="revert()" translate="rules-screen.revert-changes" class="all-caps-label cancel"></div>
|
||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" class="changes-box">
|
||||
<redaction-icon-button (action)="save()" icon="red:check" text="rules-screen.save-changes" type="primary"></redaction-icon-button>
|
||||
<div (click)="revert()" class="all-caps-label cancel" translate="rules-screen.revert-changes"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { AceEditorComponent } from 'ng2-ace-editor';
|
||||
import { RulesControllerService } from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentHasChanges } from '../../../../guards/can-deactivate.guard';
|
||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
|
||||
declare var ace;
|
||||
declare let ace;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-rules-screen',
|
||||
@ -17,14 +17,14 @@ declare var ace;
|
||||
styleUrls: ['./rules-screen.component.scss']
|
||||
})
|
||||
export class RulesScreenComponent extends ComponentHasChanges {
|
||||
public aceOptions = { showPrintMargin: false };
|
||||
public rules: string;
|
||||
public processing = true;
|
||||
aceOptions = { showPrintMargin: false };
|
||||
rules: string;
|
||||
processing = true;
|
||||
|
||||
public initialLines: string[] = [];
|
||||
public currentLines: string[] = [];
|
||||
public changedLines: number[] = [];
|
||||
public activeEditMarkers: any[] = [];
|
||||
initialLines: string[] = [];
|
||||
currentLines: string[] = [];
|
||||
changedLines: number[] = [];
|
||||
activeEditMarkers: any[] = [];
|
||||
|
||||
@ViewChild('editorComponent', { static: true })
|
||||
editorComponent: AceEditorComponent;
|
||||
@ -33,7 +33,7 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
private _fileInput: ElementRef;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _rulesControllerService: RulesControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
@ -45,19 +45,11 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
private _initialize() {
|
||||
this._rulesControllerService.downloadRules(this._appStateService.activeRuleSetId).subscribe(
|
||||
(rules) => {
|
||||
this.rules = rules.rules;
|
||||
this.revert();
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
get hasChanges(): boolean {
|
||||
return this.activeEditMarkers.length > 0;
|
||||
}
|
||||
|
||||
public textChanged($event: any) {
|
||||
textChanged($event: any) {
|
||||
this.currentLines = $event.split('\n');
|
||||
this.changedLines = [];
|
||||
this.activeEditMarkers.forEach((am) => {
|
||||
@ -72,21 +64,17 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
}
|
||||
}
|
||||
|
||||
const Range = ace.require('ace/range').Range;
|
||||
const range = ace.require('ace/range').Range;
|
||||
for (const i of this.changedLines) {
|
||||
const entry = this.currentLines[i];
|
||||
if (entry?.trim().length > 0) {
|
||||
// only mark non-empty lines
|
||||
this.activeEditMarkers.push(this.editorComponent.getEditor().getSession().addMarker(new Range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||
this.activeEditMarkers.push(this.editorComponent.getEditor().getSession().addMarker(new range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get hasChanges(): boolean {
|
||||
return this.activeEditMarkers.length > 0;
|
||||
}
|
||||
|
||||
public async save(): Promise<void> {
|
||||
async save(): Promise<void> {
|
||||
this.processing = true;
|
||||
this._rulesControllerService
|
||||
.uploadRules({
|
||||
@ -109,14 +97,14 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
);
|
||||
}
|
||||
|
||||
public revert(): void {
|
||||
revert(): void {
|
||||
this.initialLines = this.rules.split('\n');
|
||||
this.editorComponent.getEditor().setValue(this.rules);
|
||||
this.editorComponent.getEditor().clearSelection();
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
public download(): void {
|
||||
download(): void {
|
||||
const content = this.editorComponent.getEditor().getValue();
|
||||
const blob = new Blob([content], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
@ -124,7 +112,7 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
saveAs(blob, 'rules.txt');
|
||||
}
|
||||
|
||||
public upload($event): void {
|
||||
upload($event): void {
|
||||
const file = $event.target.files[0];
|
||||
const fileReader = new FileReader();
|
||||
|
||||
@ -136,4 +124,16 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
fileReader.readAsText(file);
|
||||
}
|
||||
}
|
||||
|
||||
private _initialize() {
|
||||
this._rulesControllerService.downloadRules(this._appStateService.activeRuleSetId).subscribe(
|
||||
(rules) => {
|
||||
this.rules = rules.rules;
|
||||
this.revert();
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { SmtpConfigurationControllerService, SMTPConfigurationModel } from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Component({
|
||||
@ -12,13 +12,13 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
styleUrls: ['./smtp-config-screen.component.scss']
|
||||
})
|
||||
export class SmtpConfigScreenComponent implements OnInit {
|
||||
public viewReady = false;
|
||||
public configForm: FormGroup;
|
||||
viewReady = false;
|
||||
configForm: FormGroup;
|
||||
|
||||
private _initialValue: SMTPConfigurationModel;
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _smtpConfigService: SmtpConfigurationControllerService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _dialogService: AdminDialogService,
|
||||
@ -46,21 +46,8 @@ export class SmtpConfigScreenComponent implements OnInit {
|
||||
}
|
||||
});
|
||||
}
|
||||
async ngOnInit() {
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
|
||||
this.configForm.patchValue(this._initialValue, { emitEvent: false });
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.viewReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
public get changed(): boolean {
|
||||
get changed(): boolean {
|
||||
if (!this._initialValue) return true;
|
||||
|
||||
for (const key of Object.keys(this.configForm.getRawValue())) {
|
||||
@ -72,14 +59,18 @@ export class SmtpConfigScreenComponent implements OnInit {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async save() {
|
||||
async ngOnInit() {
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.viewReady = false;
|
||||
await this._smtpConfigService.updateSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
||||
this._initialValue = this.configForm.getRawValue();
|
||||
this.viewReady = true;
|
||||
}
|
||||
|
||||
public openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
||||
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
||||
this._dialogService.openSMTPAuthConfigDialog(this.configForm.getRawValue(), (authConfig) => {
|
||||
if (authConfig) {
|
||||
this.configForm.patchValue(authConfig);
|
||||
@ -89,10 +80,10 @@ export class SmtpConfigScreenComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public async testConnection() {
|
||||
async testConnection() {
|
||||
this.viewReady = false;
|
||||
try {
|
||||
const res = await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
||||
await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('smtp-config-screen.test.success'),
|
||||
undefined,
|
||||
@ -104,4 +95,14 @@ export class SmtpConfigScreenComponent implements OnInit {
|
||||
this.viewReady = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
|
||||
this.configForm.patchValue(this._initialValue, { emitEvent: false });
|
||||
} catch (e) {
|
||||
} finally {
|
||||
this.viewReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { Component, Injector, OnInit } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { User, UserControllerService } from '@redaction/red-ui-http';
|
||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DoughnutChartConfig } from '../../../shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { TranslateChartService } from '../../../../services/translate-chart.service';
|
||||
import { BaseListingComponent } from '../../../shared/base/base-listing.component';
|
||||
import { DoughnutChartConfig } from '@shared/components/simple-doughnut-chart/simple-doughnut-chart.component';
|
||||
import { TranslateChartService } from '@services/translate-chart.service';
|
||||
import { BaseListingComponent } from '@shared/base/base-listing.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-listing-screen',
|
||||
@ -14,16 +14,15 @@ import { BaseListingComponent } from '../../../shared/base/base-listing.componen
|
||||
styleUrls: ['./user-listing-screen.component.scss']
|
||||
})
|
||||
export class UserListingScreenComponent extends BaseListingComponent<User> implements OnInit {
|
||||
viewReady = false;
|
||||
loading = false;
|
||||
collapsedDetails = false;
|
||||
chartData: DoughnutChartConfig[] = [];
|
||||
protected readonly _selectionKey = 'userId';
|
||||
|
||||
public viewReady = false;
|
||||
public loading = false;
|
||||
public collapsedDetails = false;
|
||||
public chartData: DoughnutChartConfig[] = [];
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly userService: UserService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userService: UserService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _adminDialogService: AdminDialogService,
|
||||
private readonly _userControllerService: UserControllerService,
|
||||
@ -33,15 +32,15 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
super(_injector);
|
||||
}
|
||||
|
||||
public async ngOnInit() {
|
||||
get canDeleteSelected(): boolean {
|
||||
return this.selectedEntitiesIds.indexOf(this.userService.userId) === -1;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
protected _searchField(user: any): string {
|
||||
return this.userService.getName(user);
|
||||
}
|
||||
|
||||
public openAddEditUserDialog($event: MouseEvent, user?: User) {
|
||||
openAddEditUserDialog($event: MouseEvent, user?: User) {
|
||||
$event.stopPropagation();
|
||||
this._adminDialogService.openAddEditUserDialog(user, async (result) => {
|
||||
if (result === 'DELETE') {
|
||||
@ -58,7 +57,7 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
});
|
||||
}
|
||||
|
||||
public openDeleteUserDialog(users: User[], $event?: MouseEvent) {
|
||||
openDeleteUserDialog(users: User[], $event?: MouseEvent) {
|
||||
$event?.stopPropagation();
|
||||
this._adminDialogService.openConfirmDeleteUsersDialog(users, async () => {
|
||||
this.loading = true;
|
||||
@ -67,6 +66,29 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
});
|
||||
}
|
||||
|
||||
getDisplayRoles(user: User) {
|
||||
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
|
||||
}
|
||||
|
||||
async toggleActive(user: User) {
|
||||
this.loading = true;
|
||||
user.roles = this.userService.isActive(user) ? [] : ['RED_USER'];
|
||||
await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise();
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
toggleCollapsedDetails() {
|
||||
this.collapsedDetails = !this.collapsedDetails;
|
||||
}
|
||||
|
||||
async bulkDelete() {
|
||||
this.openDeleteUserDialog(this.allEntities.filter((u) => this.isEntitySelected(u)));
|
||||
}
|
||||
|
||||
protected _searchField(user: any): string {
|
||||
return this.userService.getName(user);
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
this.allEntities = await this._userControllerService.getAllUsers().toPromise();
|
||||
this._executeSearchImmediately();
|
||||
@ -111,27 +133,4 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
].filter((type) => type.value > 0)
|
||||
);
|
||||
}
|
||||
|
||||
public getDisplayRoles(user: User) {
|
||||
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
|
||||
}
|
||||
|
||||
public async toggleActive(user: User) {
|
||||
this.loading = true;
|
||||
user.roles = this.userService.isActive(user) ? [] : ['RED_USER'];
|
||||
await this._userControllerService.addRoleToUsers(user.roles, user.userId).toPromise();
|
||||
await this._loadData();
|
||||
}
|
||||
|
||||
public toggleCollapsedDetails() {
|
||||
this.collapsedDetails = !this.collapsedDetails;
|
||||
}
|
||||
|
||||
public async bulkDelete() {
|
||||
this.openDeleteUserDialog(this.allEntities.filter((u) => this.isEntitySelected(u)));
|
||||
}
|
||||
|
||||
public get canDeleteSelected(): boolean {
|
||||
return this.selectedEntitiesIds.indexOf(this.userService.userId) === -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||
|
||||
<div class="actions flex-1">
|
||||
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="below" icon="red:close"></redaction-circle-button>
|
||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -14,31 +14,31 @@
|
||||
|
||||
<div class="content-container">
|
||||
<div #viewer class="viewer"></div>
|
||||
<div class="changes-box" *ngIf="changed && permissionsService.isAdmin()">
|
||||
<div *ngIf="changed && permissionsService.isAdmin()" class="changes-box">
|
||||
<redaction-icon-button
|
||||
(action)="save()"
|
||||
[disabled]="configForm.invalid"
|
||||
icon="red:check"
|
||||
(action)="save()"
|
||||
text="watermark-screen.action.save"
|
||||
type="primary"
|
||||
></redaction-icon-button>
|
||||
<div (click)="revert()" translate="watermark-screen.action.revert" class="all-caps-label cancel"></div>
|
||||
<div (click)="revert()" class="all-caps-label cancel" translate="watermark-screen.action.revert"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-container" redactionHasScrollbar>
|
||||
<div class="heading-xl" [translate]="'watermark-screen.title'"></div>
|
||||
<form [formGroup]="configForm" (keyup)="configChanged()">
|
||||
<div [translate]="'watermark-screen.title'" class="heading-xl"></div>
|
||||
<form (keyup)="configChanged()" [formGroup]="configForm">
|
||||
<div class="red-input-group w-300">
|
||||
<textarea
|
||||
redactionHasScrollbar
|
||||
formControlName="text"
|
||||
[placeholder]="'watermark-screen.form.text-placeholder' | translate"
|
||||
(mousemove)="triggerChanges()"
|
||||
[placeholder]="'watermark-screen.form.text-placeholder' | translate"
|
||||
class="w-full"
|
||||
formControlName="text"
|
||||
name="text"
|
||||
type="text"
|
||||
redactionHasScrollbar
|
||||
rows="4"
|
||||
type="text"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
@ -46,11 +46,11 @@
|
||||
<label class="all-caps-label mb-8" translate="watermark-screen.form.orientation"></label>
|
||||
<div class="square-options">
|
||||
<div
|
||||
(click)="setValue('orientation', option)"
|
||||
*ngFor="let option of ['VERTICAL', 'HORIZONTAL', 'DIAGONAL']"
|
||||
[class.active]="configForm.get('orientation').value === option"
|
||||
[class.disabled]="configForm.get('orientation').disabled"
|
||||
[ngClass]="option"
|
||||
(click)="setValue('orientation', option)"
|
||||
*ngFor="let option of ['VERTICAL', 'HORIZONTAL', 'DIAGONAL']"
|
||||
>
|
||||
<span>ABC</span>
|
||||
</div>
|
||||
@ -59,33 +59,33 @@
|
||||
|
||||
<div class="red-input-group">
|
||||
<label class="all-caps-label" translate="watermark-screen.form.font-size"></label>
|
||||
<mat-slider formControlName="fontSize" min="5" max="50" color="primary" (change)="configChanged()"></mat-slider>
|
||||
<mat-slider (change)="configChanged()" color="primary" formControlName="fontSize" max="50" min="5"></mat-slider>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<label class="all-caps-label" translate="watermark-screen.form.opacity"></label>
|
||||
<mat-slider formControlName="opacity" min="1" color="primary" (change)="configChanged()"></mat-slider>
|
||||
<mat-slider (change)="configChanged()" color="primary" formControlName="opacity" min="1"></mat-slider>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group w-150">
|
||||
<label class="all-caps-label mb-5" translate="watermark-screen.form.color"></label>
|
||||
<input
|
||||
formControlName="hexColor"
|
||||
class="hex-color-input"
|
||||
formControlName="hexColor"
|
||||
name="hexColor"
|
||||
type="text"
|
||||
placeholder="{{ 'add-edit-dictionary.form.color-placeholder' | translate }}"
|
||||
type="text"
|
||||
/>
|
||||
<div
|
||||
class="input-icon"
|
||||
(colorPickerChange)="setValue('hexColor', $event)"
|
||||
[class.disabled]="configForm.get('hexColor').disabled"
|
||||
[style.background]="configForm.get('hexColor').value"
|
||||
[colorPicker]="configForm.get('hexColor').value"
|
||||
[cpDisabled]="configForm.get('hexColor').disabled"
|
||||
[cpOutputFormat]="'hex'"
|
||||
[cpPosition]="'top-right'"
|
||||
[cpUseRootViewContainer]="true"
|
||||
(colorPickerChange)="setValue('hexColor', $event)"
|
||||
[style.background]="configForm.get('hexColor').value"
|
||||
class="input-icon"
|
||||
>
|
||||
<mat-icon
|
||||
*ngIf="!configForm.get('hexColor')?.value || configForm.get('hexColor').value?.length === 0"
|
||||
@ -98,9 +98,6 @@
|
||||
<label class="all-caps-label mb-8" translate="watermark-screen.form.font-type"></label>
|
||||
<div class="square-options">
|
||||
<div
|
||||
[class.active]="configForm.get('fontType').value === option.value"
|
||||
[class.disabled]="configForm.get('fontType').disabled"
|
||||
[ngClass]="option.value"
|
||||
(click)="setValue('fontType', option.value)"
|
||||
*ngFor="
|
||||
let option of [
|
||||
@ -109,6 +106,9 @@
|
||||
{ value: 'courier', display: 'Courier' }
|
||||
]
|
||||
"
|
||||
[class.active]="configForm.get('fontType').value === option.value"
|
||||
[class.disabled]="configForm.get('fontType').disabled"
|
||||
[ngClass]="option.value"
|
||||
>
|
||||
{{ option.display }}
|
||||
</div>
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { environment } from '../../../../../environments/environment';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { environment } from '@environments/environment';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { debounce } from '../../../../utils/debounce';
|
||||
import { debounce } from '@utils/debounce';
|
||||
import { WatermarkControllerService, WatermarkModel } from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../../../services/notification.service';
|
||||
import { NotificationService, NotificationType } from '@services/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { hexToRgb } from '../../../../utils/functions';
|
||||
import { hexToRgb } from '@utils/functions';
|
||||
import { BASE_HREF } from '../../../../tokens';
|
||||
|
||||
export const DEFAULT_WATERMARK: WatermarkModel = {
|
||||
@ -28,30 +28,16 @@ export const DEFAULT_WATERMARK: WatermarkModel = {
|
||||
styleUrls: ['./watermark-screen.component.scss']
|
||||
})
|
||||
export class WatermarkScreenComponent implements OnInit {
|
||||
viewReady = false;
|
||||
configForm: FormGroup;
|
||||
private _instance: WebViewerInstance;
|
||||
private _watermark: WatermarkModel = {};
|
||||
|
||||
@ViewChild('viewer', { static: true })
|
||||
private _viewer: ElementRef;
|
||||
|
||||
public viewReady = false;
|
||||
public configForm: FormGroup;
|
||||
|
||||
get changed(): boolean {
|
||||
if (this._watermark === DEFAULT_WATERMARK) {
|
||||
return true;
|
||||
}
|
||||
for (const key of Object.keys(this._watermark)) {
|
||||
if (this._watermark[key] !== this.configForm.get(key)?.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly appStateService: AppStateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly appStateService: AppStateService,
|
||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _watermarkControllerService: WatermarkControllerService,
|
||||
@ -65,31 +51,28 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
this._initForm();
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
if (this._watermark === DEFAULT_WATERMARK) {
|
||||
return true;
|
||||
}
|
||||
for (const key of Object.keys(this._watermark)) {
|
||||
if (this._watermark[key] !== this.configForm.get(key)?.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._loadWatermark();
|
||||
}
|
||||
|
||||
private _loadWatermark() {
|
||||
this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe(
|
||||
(watermark) => {
|
||||
this._watermark = watermark;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
},
|
||||
() => {
|
||||
this._watermark = DEFAULT_WATERMARK;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@debounce()
|
||||
public configChanged() {
|
||||
configChanged() {
|
||||
this._drawWatermark();
|
||||
}
|
||||
|
||||
public save() {
|
||||
save() {
|
||||
const watermark = {
|
||||
...this.configForm.getRawValue()
|
||||
};
|
||||
@ -113,12 +96,34 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
public revert() {
|
||||
revert() {
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this.configChanged();
|
||||
}
|
||||
|
||||
public triggerChanges() {}
|
||||
triggerChanges() {}
|
||||
|
||||
setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) {
|
||||
if (!this.configForm.get(type).disabled) {
|
||||
this.configForm.get(type).setValue(value);
|
||||
this.configChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private _loadWatermark() {
|
||||
this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe(
|
||||
(watermark) => {
|
||||
this._watermark = watermark;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
},
|
||||
() => {
|
||||
this._watermark = DEFAULT_WATERMARK;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadViewer() {
|
||||
if (!this._instance) {
|
||||
@ -158,16 +163,16 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
}
|
||||
|
||||
private async _drawWatermark() {
|
||||
const PDFNet = this._instance.PDFNet;
|
||||
const pdfNet = this._instance.PDFNet;
|
||||
const document = await this._instance.docViewer.getDocument().getPDFDoc();
|
||||
|
||||
await PDFNet.runWithCleanup(
|
||||
await pdfNet.runWithCleanup(
|
||||
async () => {
|
||||
await document.lock();
|
||||
|
||||
const pageSet = await PDFNet.PageSet.createSinglePage(1);
|
||||
const pageSet = await pdfNet.PageSet.createSinglePage(1);
|
||||
|
||||
await PDFNet.Stamper.deleteStamps(document, pageSet);
|
||||
await pdfNet.Stamper.deleteStamps(document, pageSet);
|
||||
|
||||
const text = this.configForm.get('text').value || '';
|
||||
const fontSize = this.configForm.get('fontSize').value;
|
||||
@ -178,8 +183,8 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
|
||||
const rgbColor = hexToRgb(color);
|
||||
|
||||
const stamper = await PDFNet.Stamper.create(3, fontSize, 0);
|
||||
await stamper.setFontColor(await PDFNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255));
|
||||
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
|
||||
await stamper.setFontColor(await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255));
|
||||
await stamper.setOpacity(opacity / 100);
|
||||
|
||||
switch (orientation) {
|
||||
@ -195,7 +200,7 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
await stamper.setRotation(-45);
|
||||
}
|
||||
|
||||
const font = await PDFNet.Font.createAndEmbed(document, this._convertFont(fontType));
|
||||
const font = await pdfNet.Font.createAndEmbed(document, this._convertFont(fontType));
|
||||
await stamper.setFont(font);
|
||||
await stamper.setTextAlignment(0);
|
||||
await stamper.stampText(document, text, pageSet);
|
||||
@ -219,13 +224,6 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
public setValue(type: 'fontType' | 'orientation' | 'hexColor', value: any) {
|
||||
if (!this.configForm.get(type).disabled) {
|
||||
this.configForm.get(type).setValue(value);
|
||||
this.configChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private _convertFont(fontType: any): number {
|
||||
switch (fontType) {
|
||||
case 'times-new-roman':
|
||||
|
||||
@ -16,9 +16,9 @@ import {
|
||||
import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
|
||||
import { AddEditDictionaryDialogComponent } from '../dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
|
||||
import { AddEditRuleSetDialogComponent } from '../dialogs/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component';
|
||||
import { NotificationService } from '../../../services/notification.service';
|
||||
import { ConfirmationDialogComponent } from '../../shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { NotificationService } from '@services/notification.service';
|
||||
import { ConfirmationDialogComponent } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ConfirmDeleteFileAttributeDialogComponent } from '../dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
|
||||
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -53,7 +53,7 @@ export class AdminDialogService {
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService
|
||||
) {}
|
||||
|
||||
public openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(async (result) => {
|
||||
@ -65,7 +65,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openDeleteRuleSetDialog($event: MouseEvent, ruleSet: RuleSetModel, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
openDeleteRuleSetDialog($event: MouseEvent, ruleSet: RuleSetModel, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(async (result) => {
|
||||
@ -77,7 +77,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAddEditDictionaryDialog(dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<AddEditDictionaryDialogComponent> {
|
||||
openAddEditDictionaryDialog(dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<AddEditDictionaryDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditDictionaryDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: { dictionary, ruleSetId },
|
||||
@ -93,7 +93,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openEditColorsDialog(colors: Colors, colorKey: string, ruleSetId: string, cb?: Function): MatDialogRef<EditColorDialogComponent> {
|
||||
openEditColorsDialog(colors: Colors, colorKey: string, ruleSetId: string, cb?: Function): MatDialogRef<EditColorDialogComponent> {
|
||||
const ref = this._dialog.open(EditColorDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: { colors, colorKey, ruleSetId },
|
||||
@ -109,7 +109,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAddEditRuleSetDialog(ruleSet: RuleSetModel, cb?: Function): MatDialogRef<AddEditRuleSetDialogComponent> {
|
||||
openAddEditRuleSetDialog(ruleSet: RuleSetModel, cb?: Function): MatDialogRef<AddEditRuleSetDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditRuleSetDialogComponent, {
|
||||
...dialogConfig,
|
||||
width: '900px',
|
||||
@ -126,7 +126,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openImportFileAttributeCSVDialog(
|
||||
openImportFileAttributeCSVDialog(
|
||||
csv: File,
|
||||
ruleSetId: string,
|
||||
existingConfiguration: FileAttributesConfig,
|
||||
@ -146,11 +146,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAddEditFileAttributeDialog(
|
||||
fileAttribute: FileAttributeConfig,
|
||||
ruleSetId: string,
|
||||
cb?: Function
|
||||
): MatDialogRef<AddEditFileAttributeDialogComponent> {
|
||||
openAddEditFileAttributeDialog(fileAttribute: FileAttributeConfig, ruleSetId: string, cb?: Function): MatDialogRef<AddEditFileAttributeDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditFileAttributeDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: { fileAttribute, ruleSetId },
|
||||
@ -166,7 +162,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openConfirmDeleteFileAttributeDialog(
|
||||
openConfirmDeleteFileAttributeDialog(
|
||||
fileAttribute: FileAttributeConfig,
|
||||
ruleSetId: string,
|
||||
cb?: Function
|
||||
@ -186,7 +182,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openSMTPAuthConfigDialog(smtpConfig: SMTPConfigurationModel, cb?: Function): MatDialogRef<SmtpAuthDialogComponent> {
|
||||
openSMTPAuthConfigDialog(smtpConfig: SMTPConfigurationModel, cb?: Function): MatDialogRef<SmtpAuthDialogComponent> {
|
||||
const ref = this._dialog.open(SmtpAuthDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: smtpConfig,
|
||||
@ -202,7 +198,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openAddEditUserDialog(user?: User, cb?: Function): MatDialogRef<AddEditUserDialogComponent> {
|
||||
openAddEditUserDialog(user?: User, cb?: Function): MatDialogRef<AddEditUserDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditUserDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: user,
|
||||
@ -218,7 +214,7 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public openConfirmDeleteUsersDialog(users: User[], cb?: Function): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
||||
openConfirmDeleteUsersDialog(users: User[], cb?: Function): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
||||
const ref = this._dialog.open(ConfirmDeleteUsersDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: users,
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { AppLoadStateService } from '../../services/app-load-state.service';
|
||||
import { AppConfigKey, AppConfigService } from '../app-config/app-config.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
||||
import { BASE_HREF } from '../../tokens';
|
||||
|
||||
@Injectable({
|
||||
@ -21,7 +21,7 @@ export class AuthGuard extends KeycloakAuthGuard {
|
||||
super(_router, _keycloak);
|
||||
}
|
||||
|
||||
public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (!this.authenticated) {
|
||||
await this._keycloak.login({
|
||||
idpHint: this._appConfigService.getConfig(AppConfigKey.OAUTH_IDP_HINT, null),
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { AppConfigModule } from '../app-config/app-config.module';
|
||||
import { AppConfigModule } from '@app-config/app-config.module';
|
||||
|
||||
import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular';
|
||||
import { AppConfigKey, AppConfigService } from '../app-config/app-config.service';
|
||||
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
||||
import { BASE_HREF } from '../../tokens';
|
||||
|
||||
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) {
|
||||
return () => {
|
||||
return appConfigService
|
||||
return () =>
|
||||
appConfigService
|
||||
.loadAppConfig()
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
@ -33,7 +33,6 @@ export function keycloakInitializer(keycloak: KeycloakService, appConfigService:
|
||||
};
|
||||
return keycloak.init(options).then(() => configureAutomaticRedirectToLoginScreen(keycloak));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakService) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { AppLoadStateService } from '../../services/app-load-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
|
||||
@ -9,7 +9,7 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
exports: [MatIconModule]
|
||||
})
|
||||
export class IconsModule {
|
||||
constructor(private iconRegistry: MatIconRegistry, private sanitizer: DomSanitizer) {
|
||||
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
|
||||
const icons = [
|
||||
'add',
|
||||
'analyse',
|
||||
@ -84,7 +84,7 @@ export class IconsModule {
|
||||
];
|
||||
|
||||
for (const icon of icons) {
|
||||
iconRegistry.addSvgIconInNamespace('red', icon, sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${icon}.svg`));
|
||||
_iconRegistry.addSvgIconInNamespace('red', icon, _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${icon}.svg`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,87 +1,87 @@
|
||||
<div [class.visible]="menuOpen" *ngIf="canPerformAnnotationActions" class="annotation-actions">
|
||||
<div *ngIf="canPerformAnnotationActions" [class.visible]="menuOpen" class="annotation-actions">
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.forceRedaction($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
*ngIf="annotationPermissions.canForceRedaction"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.force-redaction.label"
|
||||
icon="red:thumb-up"
|
||||
tooltip="annotation-actions.force-redaction.label"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.accept-recommendation.label"
|
||||
icon="red:check"
|
||||
tooltip="annotation-actions.accept-recommendation.label"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
*ngIf="annotationPermissions.canMarkTextOnlyAsFalsePositive && !annotationPermissions.canPerformMultipleRemoveActions"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.remove-annotation.false-positive"
|
||||
icon="red:thumb-down"
|
||||
tooltip="annotation-actions.remove-annotation.false-positive"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.acceptSuggestion($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.accept-suggestion.label"
|
||||
icon="red:check"
|
||||
tooltip="annotation-actions.accept-suggestion.label"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.undoDirectAction($event, [annotation], annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canUndo"
|
||||
type="dark-bg"
|
||||
icon="red:undo"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.undo"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="hideAnnotation($event)"
|
||||
*ngIf="annotation.isImage && viewerAnnotation?.isVisible()"
|
||||
type="dark-bg"
|
||||
icon="red:visibility-off"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.hide"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="showAnnotation($event)"
|
||||
*ngIf="annotation.isImage && !viewerAnnotation?.isVisible()"
|
||||
type="dark-bg"
|
||||
icon="red:visibility"
|
||||
tooltipPosition="before"
|
||||
tooltip="annotation-actions.show"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.rejectSuggestion($event, [annotation], annotationsChanged)"
|
||||
type="dark-bg"
|
||||
icon="red:close"
|
||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||
tooltipPosition="before"
|
||||
icon="red:close"
|
||||
tooltip="annotation-actions.reject-suggestion"
|
||||
tooltipPosition="before"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-annotation-remove-actions
|
||||
[annotations]="[annotation]"
|
||||
[(menuOpen)]="menuOpen"
|
||||
[annotationsChanged]="annotationsChanged"
|
||||
[annotations]="[annotation]"
|
||||
></redaction-annotation-remove-actions>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { AnnotationPermissions } from '../../../../models/file/annotation.permissions';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { Annotations, WebViewerInstance } from '@pdftron/webviewer';
|
||||
|
||||
@ -27,6 +27,10 @@ export class AnnotationActionsComponent implements OnInit {
|
||||
private _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
get viewerAnnotation(): Annotations.Annotation {
|
||||
return this.viewer.annotManager.getAnnotationById(this.annotation.id);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.annotationPermissions = AnnotationPermissions.forUser(
|
||||
this._permissionsService.isManagerAndOwner(),
|
||||
@ -35,17 +39,13 @@ export class AnnotationActionsComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
public get viewerAnnotation(): Annotations.Annotation {
|
||||
return this.viewer.annotManager.getAnnotationById(this.annotation.id);
|
||||
}
|
||||
|
||||
public hideAnnotation($event: MouseEvent) {
|
||||
hideAnnotation($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.viewer.annotManager.hideAnnotations([this.viewerAnnotation]);
|
||||
this.viewer.annotManager.deselectAllAnnotations();
|
||||
}
|
||||
|
||||
public showAnnotation($event: MouseEvent) {
|
||||
showAnnotation($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.viewer.annotManager.showAnnotations([this.viewerAnnotation]);
|
||||
this.viewer.annotManager.deselectAllAnnotations();
|
||||
|
||||
@ -1,37 +1,37 @@
|
||||
<redaction-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
*ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere && permissions.canNotPerformMultipleRemoveActions"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
*ngIf="permissions.canPerformMultipleRemoveActions"
|
||||
(action)="openMenu($event)"
|
||||
*ngIf="permissions.canPerformMultipleRemoveActions"
|
||||
[class.active]="menuOpen"
|
||||
[matMenuTriggerFor]="menu"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
tooltip="annotation-actions.suggest-remove-annotation"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||
<div (click)="suggestRemoveAnnotations($event, true)" mat-menu-item *ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary">
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="dictionaryColor"></redaction-annotation-icon>
|
||||
<div (click)="suggestRemoveAnnotations($event, true)" *ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary" mat-menu-item>
|
||||
<redaction-annotation-icon [color]="dictionaryColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
||||
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||
</div>
|
||||
<div (click)="suggestRemoveAnnotations($event, false)" mat-menu-item *ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere">
|
||||
<redaction-annotation-icon [type]="'rhombus'" [label]="'S'" [color]="suggestionColor"></redaction-annotation-icon>
|
||||
<div (click)="suggestRemoveAnnotations($event, false)" *ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere" mat-menu-item>
|
||||
<redaction-annotation-icon [color]="suggestionColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
||||
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
||||
</div>
|
||||
|
||||
<div (click)="markAsFalsePositive($event)" mat-menu-item *ngIf="permissions.canMarkAsFalsePositive">
|
||||
<mat-icon svgIcon="red:thumb-down" class="false-positive-icon"></mat-icon>
|
||||
<div (click)="markAsFalsePositive($event)" *ngIf="permissions.canMarkAsFalsePositive" mat-menu-item>
|
||||
<mat-icon class="false-positive-icon" svgIcon="red:thumb-down"></mat-icon>
|
||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
||||
import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { AnnotationPermissions } from '../../../../models/file/annotation.permissions';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { MatMenuTrigger } from '@angular/material/menu';
|
||||
|
||||
@Component({
|
||||
@ -11,7 +11,7 @@ import { MatMenuTrigger } from '@angular/material/menu';
|
||||
templateUrl: './annotation-remove-actions.component.html',
|
||||
styleUrls: ['./annotation-remove-actions.component.scss']
|
||||
})
|
||||
export class AnnotationRemoveActionsComponent implements OnInit {
|
||||
export class AnnotationRemoveActionsComponent {
|
||||
@Output() menuOpenChange = new EventEmitter<boolean>();
|
||||
@Input() annotationsChanged: EventEmitter<AnnotationWrapper>;
|
||||
@Input() menuOpen: boolean;
|
||||
@ -19,7 +19,7 @@ export class AnnotationRemoveActionsComponent implements OnInit {
|
||||
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
||||
|
||||
@ViewChild(MatMenuTrigger) matMenuTrigger: MatMenuTrigger;
|
||||
public permissions: {
|
||||
permissions: {
|
||||
canRemoveOrSuggestToRemoveOnlyHere: boolean;
|
||||
canPerformMultipleRemoveActions: boolean;
|
||||
canNotPerformMultipleRemoveActions: boolean;
|
||||
@ -28,49 +28,47 @@ export class AnnotationRemoveActionsComponent implements OnInit {
|
||||
};
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
readonly appStateService: AppStateService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
private _annotations: AnnotationWrapper[];
|
||||
|
||||
public get annotations(): AnnotationWrapper[] {
|
||||
get annotations(): AnnotationWrapper[] {
|
||||
return this._annotations;
|
||||
}
|
||||
|
||||
@Input()
|
||||
public set annotations(value: AnnotationWrapper[]) {
|
||||
set annotations(value: AnnotationWrapper[]) {
|
||||
this._annotations = value.filter((a) => a !== undefined);
|
||||
this._setPermissions();
|
||||
}
|
||||
|
||||
public get dictionaryColor() {
|
||||
get dictionaryColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion-add-dictionary');
|
||||
}
|
||||
|
||||
public get suggestionColor() {
|
||||
get suggestionColor() {
|
||||
return this.appStateService.getDictionaryColor('suggestion');
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public openMenu($event: MouseEvent) {
|
||||
openMenu($event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.matMenuTrigger.openMenu();
|
||||
this.menuOpenChange.emit(true);
|
||||
}
|
||||
|
||||
public onMenuClosed() {
|
||||
onMenuClosed() {
|
||||
this.menuOpenChange.emit(false);
|
||||
}
|
||||
|
||||
public suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||
suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||
$event.stopPropagation();
|
||||
this._annotationActionsService.suggestRemoveAnnotation($event, this.annotations, removeFromDict, this.annotationsChanged);
|
||||
}
|
||||
|
||||
public markAsFalsePositive($event) {
|
||||
markAsFalsePositive($event) {
|
||||
this._annotationActionsService.markAsFalsePositive($event, this.annotations, this.annotationsChanged);
|
||||
}
|
||||
|
||||
|
||||
@ -2,31 +2,31 @@
|
||||
<redaction-circle-button
|
||||
(action)="delete()"
|
||||
*ngIf="canDelete"
|
||||
icon="red:trash"
|
||||
tooltip="project-overview.bulk.delete"
|
||||
type="dark-bg"
|
||||
icon="red:trash"
|
||||
></redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="assign()"
|
||||
*ngIf="canAssign"
|
||||
icon="red:assign"
|
||||
tooltip="project-overview.bulk.assign"
|
||||
type="dark-bg"
|
||||
icon="red:assign"
|
||||
></redaction-circle-button>
|
||||
|
||||
<redaction-file-download-btn [project]="project" [file]="selectedFiles"> </redaction-file-download-btn>
|
||||
<redaction-file-download-btn [file]="selectedFiles" [project]="project"></redaction-file-download-btn>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="setToUnderApproval()"
|
||||
*ngIf="canSetToUnderApproval"
|
||||
icon="red:ready-for-approval"
|
||||
tooltip="project-overview.under-approval"
|
||||
type="dark-bg"
|
||||
icon="red:ready-for-approval"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button (action)="setToUnderReview()" *ngIf="canSetToUnderReview" tooltip="project-overview.under-review" type="dark-bg" icon="red:undo">
|
||||
<redaction-circle-button (action)="setToUnderReview()" *ngIf="canSetToUnderReview" icon="red:undo" tooltip="project-overview.under-review" type="dark-bg">
|
||||
</redaction-circle-button>
|
||||
|
||||
<!-- Approved-->
|
||||
@ -34,24 +34,24 @@
|
||||
(action)="approveDocuments()"
|
||||
*ngIf="isReadyForApproval"
|
||||
[disabled]="!canApprove"
|
||||
type="dark-bg"
|
||||
icon="red:approved"
|
||||
[tooltip]="canApprove ? 'project-overview.approve' : 'project-overview.approve-disabled'"
|
||||
icon="red:approved"
|
||||
type="dark-bg"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<!-- Back to approval -->
|
||||
<redaction-circle-button (action)="setToUnderApproval()" *ngIf="canUndoApproval" tooltip="project-overview.under-approval" type="dark-bg" icon="red:undo">
|
||||
<redaction-circle-button (action)="setToUnderApproval()" *ngIf="canUndoApproval" icon="red:undo" tooltip="project-overview.under-approval" type="dark-bg">
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button (action)="ocr()" *ngIf="canOcr" tooltip="project-overview.ocr-file" type="dark-bg" icon="red:ocr"></redaction-circle-button>
|
||||
<redaction-circle-button (action)="ocr()" *ngIf="canOcr" icon="red:ocr" tooltip="project-overview.ocr-file" type="dark-bg"></redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="reanalyse()"
|
||||
*ngIf="canReanalyse"
|
||||
icon="red:refresh"
|
||||
tooltip="project-overview.bulk.reanalyse"
|
||||
type="dark-bg"
|
||||
icon="red:refresh"
|
||||
></redaction-circle-button>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { StatusOverlayService } from '../../../upload-download/services/status-overlay.service';
|
||||
import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
|
||||
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||
|
||||
@Component({
|
||||
@ -16,8 +16,8 @@ import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||
})
|
||||
export class ProjectOverviewBulkActionsComponent {
|
||||
@Input() selectedFileIds: string[];
|
||||
@Output() private reload = new EventEmitter();
|
||||
public loading = false;
|
||||
loading = false;
|
||||
@Output() private _reload = new EventEmitter();
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
@ -39,52 +39,76 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
return this.selectedFileIds.map((fileId) => this._appStateService.getFileById(this._appStateService.activeProject.project.projectId, fileId));
|
||||
}
|
||||
|
||||
public get areAllFilesSelected() {
|
||||
get areAllFilesSelected() {
|
||||
return this._appStateService.activeProject.files.length !== 0 && this.selectedFileIds.length === this._appStateService.activeProject.files.length;
|
||||
}
|
||||
|
||||
public get areSomeFilesSelected() {
|
||||
get areSomeFilesSelected() {
|
||||
return this.selectedFileIds.length > 0;
|
||||
}
|
||||
|
||||
public get canDelete() {
|
||||
get canDelete() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file), true);
|
||||
}
|
||||
|
||||
public get canAssign() {
|
||||
get canAssign() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignReviewer(file), true);
|
||||
}
|
||||
|
||||
public get canReanalyse() {
|
||||
get canReanalyse() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file), true);
|
||||
}
|
||||
|
||||
public get canOcr() {
|
||||
get canOcr() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canOcrFile(file), true);
|
||||
}
|
||||
|
||||
public get fileStatuses() {
|
||||
get fileStatuses() {
|
||||
return this.selectedFiles.map((file) => file.fileStatus.status);
|
||||
}
|
||||
|
||||
public delete() {
|
||||
// Under review
|
||||
get canSetToUnderReview() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
|
||||
}
|
||||
|
||||
// Under approval
|
||||
get canSetToUnderApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
|
||||
}
|
||||
|
||||
// Approve
|
||||
get isReadyForApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
|
||||
}
|
||||
|
||||
get canApprove() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true);
|
||||
}
|
||||
|
||||
// Undo approval
|
||||
get canUndoApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.loading = true;
|
||||
this._dialogService.openDeleteFilesDialog(null, this._appStateService.activeProject.project.projectId, this.selectedFileIds, () => {
|
||||
this.reload.emit();
|
||||
this._reload.emit();
|
||||
this.loading = false;
|
||||
this.selectedFileIds.splice(0, this.selectedFileIds.length);
|
||||
});
|
||||
}
|
||||
|
||||
public assign() {
|
||||
assign() {
|
||||
this.loading = true;
|
||||
this._dialogService.openBulkAssignFileReviewerDialog(this.selectedFileIds, () => {
|
||||
this.reload.emit();
|
||||
this._reload.emit();
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
public async reanalyse() {
|
||||
async reanalyse() {
|
||||
const fileIds = this.selectedFiles.filter((file) => this._permissionsService.fileRequiresReanalysis(file)).map((file) => file.fileId);
|
||||
this._performBulkAction(this._reanalysisControllerService.reanalyzeFilesForProject(fileIds, this._appStateService.activeProject.projectId));
|
||||
}
|
||||
@ -93,46 +117,22 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
this._performBulkAction(this._fileActionService.ocrFile(this.selectedFiles));
|
||||
}
|
||||
|
||||
// Under review
|
||||
public get canSetToUnderReview() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
|
||||
}
|
||||
|
||||
public setToUnderReview() {
|
||||
setToUnderReview() {
|
||||
this._performBulkAction(this._fileActionService.setFileUnderReview(this.selectedFiles));
|
||||
}
|
||||
|
||||
// Under approval
|
||||
public get canSetToUnderApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
|
||||
}
|
||||
|
||||
public setToUnderApproval() {
|
||||
setToUnderApproval() {
|
||||
this._performBulkAction(this._fileActionService.setFileUnderApproval(this.selectedFiles));
|
||||
}
|
||||
|
||||
// Approve
|
||||
public get isReadyForApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
|
||||
}
|
||||
|
||||
public get canApprove() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true);
|
||||
}
|
||||
|
||||
public approveDocuments() {
|
||||
approveDocuments() {
|
||||
this._performBulkAction(this._fileActionService.setFileApproved(this.selectedFiles));
|
||||
}
|
||||
|
||||
// Undo approval
|
||||
public get canUndoApproval() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
|
||||
}
|
||||
|
||||
private _performBulkAction(obs: Observable<any>) {
|
||||
this.loading = true;
|
||||
obs.subscribe().add(() => {
|
||||
this.reload.emit();
|
||||
this._reload.emit();
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
<form (submit)="addComment()" *ngIf="addingComment && permissionsService.canAddComment()" [formGroup]="commentForm">
|
||||
<div class="red-input-group">
|
||||
<input [placeholder]="translateService.instant('comments.add-comment')" formControlName="comment" name="comment" type="text" class="w-full" />
|
||||
<input [placeholder]="translateService.instant('comments.add-comment')" class="w-full" formControlName="comment" name="comment" type="text" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
.comment-icon.comment-owner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.trash-icon.comment-owner {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
@ -2,11 +2,11 @@ import { ChangeDetectorRef, Component, Input } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { Comment } from '@redaction/red-ui-http';
|
||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
||||
import { AnnotationWrapper } from '../../../../models/file/annotation.wrapper';
|
||||
import { UserService } from '../../../../services/user.service';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-comments',
|
||||
@ -14,14 +14,14 @@ import { PermissionsService } from '../../../../services/permissions.service';
|
||||
styleUrls: ['./comments.component.scss']
|
||||
})
|
||||
export class CommentsComponent {
|
||||
@Input() public annotation: AnnotationWrapper;
|
||||
public expanded = false;
|
||||
public commentForm: FormGroup;
|
||||
public addingComment = false;
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
expanded = false;
|
||||
commentForm: FormGroup;
|
||||
addingComment = false;
|
||||
|
||||
constructor(
|
||||
public readonly translateService: TranslateService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
readonly translateService: TranslateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
@ -37,7 +37,7 @@ export class CommentsComponent {
|
||||
return !this.annotation.isChangeLogRemoved;
|
||||
}
|
||||
|
||||
public toggleExpandComments($event: MouseEvent): void {
|
||||
toggleExpandComments($event: MouseEvent): void {
|
||||
$event.stopPropagation();
|
||||
if (!this.annotation.comments.length) {
|
||||
return;
|
||||
@ -46,7 +46,7 @@ export class CommentsComponent {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public toggleAddingComment($event?: MouseEvent): void {
|
||||
toggleAddingComment($event?: MouseEvent): void {
|
||||
$event?.stopPropagation();
|
||||
this.addingComment = !this.addingComment;
|
||||
if (this.addingComment) {
|
||||
@ -55,7 +55,7 @@ export class CommentsComponent {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public addComment(): void {
|
||||
addComment(): void {
|
||||
const value = this.commentForm.value.comment;
|
||||
if (value) {
|
||||
this._manualAnnotationService.addComment(value, this.annotation.id).subscribe((commentResponse) => {
|
||||
@ -70,7 +70,7 @@ export class CommentsComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public deleteComment(comment: Comment): void {
|
||||
deleteComment(comment: Comment): void {
|
||||
this._manualAnnotationService.deleteComment(comment.id, this.annotation.id).subscribe(() => {
|
||||
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
||||
if (!this.annotation.comments.length) {
|
||||
@ -79,11 +79,11 @@ export class CommentsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
public isCommentOwner(comment: Comment): boolean {
|
||||
isCommentOwner(comment: Comment): boolean {
|
||||
return comment.user === this._userService.userId;
|
||||
}
|
||||
|
||||
public getOwnerName(comment: Comment): string {
|
||||
getOwnerName(comment: Comment): string {
|
||||
return this._userService.getNameForId(comment.user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
|
||||
<div>
|
||||
<redaction-circle-button
|
||||
icon="red:edit"
|
||||
(action)="edit()"
|
||||
tooltipPosition="before"
|
||||
icon="red:edit"
|
||||
tooltip="file-preview.tabs.document-info.edit"
|
||||
tooltipPosition="before"
|
||||
></redaction-circle-button>
|
||||
<redaction-circle-button
|
||||
icon="red:close"
|
||||
(action)="closeDocumentInfoView.emit()"
|
||||
tooltipPosition="before"
|
||||
icon="red:close"
|
||||
tooltip="file-preview.tabs.document-info.close"
|
||||
tooltipPosition="before"
|
||||
></redaction-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-content" redactionHasScrollbar>
|
||||
<div class="section">
|
||||
<div class="attribute" *ngFor="let attr of fileAttributesConfig?.fileAttributeConfigs">
|
||||
<div *ngFor="let attr of fileAttributesConfig?.fileAttributeConfigs" class="attribute">
|
||||
<div class="small-label">{{ attr.label }}:</div>
|
||||
<div>{{ (file.fileAttributes?.attributeIdToValue)[attr.id] || '-' }}</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { FileAttributesConfig, FileStatus } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||
|
||||
@Component({
|
||||
@ -8,27 +8,25 @@ import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||
templateUrl: './document-info.component.html',
|
||||
styleUrls: ['./document-info.component.scss']
|
||||
})
|
||||
export class DocumentInfoComponent implements OnInit {
|
||||
export class DocumentInfoComponent {
|
||||
@Input() file: FileStatus;
|
||||
@Output() closeDocumentInfoView = new EventEmitter();
|
||||
|
||||
public fileAttributesConfig: FileAttributesConfig;
|
||||
fileAttributesConfig: FileAttributesConfig;
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: ProjectsDialogService) {
|
||||
this.fileAttributesConfig = this._appStateService.activeFileAttributesConfig;
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public get project() {
|
||||
get project() {
|
||||
return this._appStateService.getProjectById(this.file.projectId);
|
||||
}
|
||||
|
||||
public edit() {
|
||||
this._dialogService.openDocumentInfoDialog(this.file);
|
||||
}
|
||||
|
||||
public get ruleSetName(): string {
|
||||
get ruleSetName(): string {
|
||||
return this._appStateService.getRuleSetById(this.project.ruleSetId).name;
|
||||
}
|
||||
|
||||
edit() {
|
||||
this._dialogService.openDocumentInfoDialog(this.file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="action-buttons" [class.active]="actionMenuOpen" *ngIf="screen === 'project-overview'">
|
||||
<div *ngIf="screen === 'project-overview'" [class.active]="actionMenuOpen" class="action-buttons">
|
||||
<ng-container *ngTemplateOutlet="actions"></ng-container>
|
||||
<redaction-status-bar
|
||||
*ngIf="fileStatus.isWorkable"
|
||||
@ -41,21 +41,21 @@
|
||||
|
||||
<!-- download redacted file-->
|
||||
<redaction-file-download-btn
|
||||
[file]="fileStatus"
|
||||
[project]="appStateService.activeProject"
|
||||
[tooltipClass]="'small'"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[type]="buttonType"
|
||||
[project]="appStateService.activeProject"
|
||||
[file]="fileStatus"
|
||||
>
|
||||
</redaction-file-download-btn>
|
||||
|
||||
<redaction-circle-button
|
||||
*ngIf="screen === 'file-preview'"
|
||||
(action)="toggleViewDocumentInfo()"
|
||||
*ngIf="screen === 'file-preview'"
|
||||
[attr.aria-expanded]="activeDocumentInfo"
|
||||
icon="red:status-info"
|
||||
tooltip="file-preview.document-info"
|
||||
tooltipPosition="below"
|
||||
icon="red:status-info"
|
||||
[attr.aria-expanded]="activeDocumentInfo"
|
||||
></redaction-circle-button>
|
||||
|
||||
<!-- Ready for approval-->
|
||||
@ -86,9 +86,9 @@
|
||||
*ngIf="permissionsService.isReadyForApproval(fileStatus)"
|
||||
[disabled]="!permissionsService.canApprove(fileStatus)"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="permissionsService.canApprove(fileStatus) ? 'project-overview.approve' : 'project-overview.approve-disabled'"
|
||||
[type]="buttonType"
|
||||
icon="red:approved"
|
||||
[tooltip]="permissionsService.canApprove(fileStatus) ? 'project-overview.approve' : 'project-overview.approve-disabled'"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
@ -117,10 +117,10 @@
|
||||
<redaction-circle-button
|
||||
(action)="reanalyseFile($event, fileStatus, 100)"
|
||||
*ngIf="permissionsService.canReanalyseFile(fileStatus) && screen === 'file-preview'"
|
||||
tooltipPosition="below"
|
||||
icon="red:refresh"
|
||||
tooltip="file-preview.reanalyse-notification"
|
||||
tooltipClass="warn small"
|
||||
tooltipPosition="below"
|
||||
type="warn"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
@ -140,11 +140,11 @@
|
||||
<div class="red-input-group">
|
||||
<mat-slide-toggle
|
||||
(click)="toggleAnalysis($event)"
|
||||
[disabled]="!permissionsService.isManager()"
|
||||
[checked]="!fileStatus?.isExcluded"
|
||||
[matTooltip]="toggleTooltip | translate"
|
||||
[matTooltipPosition]="tooltipPosition"
|
||||
[class.mr-24]="screen === 'project-overview'"
|
||||
[disabled]="!permissionsService.isManager()"
|
||||
[matTooltipPosition]="tooltipPosition"
|
||||
[matTooltip]="toggleTooltip | translate"
|
||||
color="primary"
|
||||
>
|
||||
</mat-slide-toggle>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { PermissionsService } from '../../../../services/permissions.service';
|
||||
import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper';
|
||||
import { AppStateService } from '../../../../state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
import { ProjectsDialogService } from '../../services/projects-dialog.service';
|
||||
|
||||
@ -19,12 +19,28 @@ export class FileActionsComponent implements OnInit {
|
||||
screen: 'file-preview' | 'project-overview';
|
||||
|
||||
constructor(
|
||||
public readonly permissionsService: PermissionsService,
|
||||
public readonly appStateService: AppStateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly appStateService: AppStateService,
|
||||
private readonly _dialogService: ProjectsDialogService,
|
||||
private readonly _fileActionService: FileActionService
|
||||
) {}
|
||||
|
||||
get tooltipPosition() {
|
||||
return this.screen === 'file-preview' ? 'below' : 'above';
|
||||
}
|
||||
|
||||
get buttonType() {
|
||||
return this.screen === 'file-preview' ? 'default' : 'dark-bg';
|
||||
}
|
||||
|
||||
get toggleTooltip(): string {
|
||||
if (!this.permissionsService.isManager()) {
|
||||
return 'file-preview.toggle-analysis.only-managers';
|
||||
}
|
||||
|
||||
return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.fileStatus) {
|
||||
this.fileStatus = this.appStateService.activeFile;
|
||||
@ -39,18 +55,10 @@ export class FileActionsComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public toggleViewDocumentInfo() {
|
||||
toggleViewDocumentInfo() {
|
||||
this.actionPerformed.emit('view-document-info');
|
||||
}
|
||||
|
||||
public get tooltipPosition() {
|
||||
return this.screen === 'file-preview' ? 'below' : 'above';
|
||||
}
|
||||
|
||||
public get buttonType() {
|
||||
return this.screen === 'file-preview' ? 'default' : 'dark-bg';
|
||||
}
|
||||
|
||||
openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
|
||||
this._dialogService.openDeleteFilesDialog($event, fileStatusWrapper.projectId, [fileStatusWrapper.fileId], () => {
|
||||
this.actionPerformed.emit('delete');
|
||||
@ -110,12 +118,4 @@ export class FileActionsComponent implements OnInit {
|
||||
await this.appStateService.getFiles();
|
||||
this.actionPerformed.emit(this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis');
|
||||
}
|
||||
|
||||
get toggleTooltip(): string {
|
||||
if (!this.permissionsService.isManager()) {
|
||||
return 'file-preview.toggle-analysis.only-managers';
|
||||
}
|
||||
|
||||
return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable';
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user