Pull request #186: Updates
Merge in RED/ui from updates to master * commit 'b3ce5d7914cf0e9bed1bce12008cb52b829163ac': other deps updates nx updates update nx & eslint plugins update other dependencies update angular material and fix update errors migrate to ng12 nx updates set max line length to 100 and set indent size for .json files to 2
This commit is contained in:
commit
61e454fa72
@ -8,7 +8,11 @@ indent_size = 4
|
|||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
ij_html_quote_style = double
|
ij_html_quote_style = double
|
||||||
|
max_line_length = 100
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
max_line_length = off
|
max_line_length = off
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[{*.json, .prettierrc, .eslintrc}]
|
||||||
|
indent_size = 2
|
||||||
|
|||||||
383
.eslintrc.json
383
.eslintrc.json
@ -1,182 +1,205 @@
|
|||||||
{
|
{
|
||||||
"root": true,
|
"root": true,
|
||||||
"ignorePatterns": ["**/*"],
|
"ignorePatterns": ["**/*"],
|
||||||
"plugins": ["@nrwl/nx"],
|
"plugins": ["@nrwl/nx"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@nrwl/nx/enforce-module-boundaries": [
|
"@nrwl/nx/enforce-module-boundaries": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
"enforceBuildableLibDependency": true,
|
"enforceBuildableLibDependency": true,
|
||||||
"allow": [],
|
"allow": [
|
||||||
"depConstraints": [
|
"@redaction/red-ui-http",
|
||||||
{
|
"@redaction/red-cache",
|
||||||
"sourceTag": "*",
|
"@services/**",
|
||||||
"onlyDependOnLibsWithTags": ["*"]
|
"@components/**",
|
||||||
}
|
"@guards/**",
|
||||||
]
|
"@i18n/**",
|
||||||
}
|
"@state/**",
|
||||||
]
|
"@utils/**",
|
||||||
}
|
"@models/**",
|
||||||
},
|
"@environments/**",
|
||||||
{
|
"@shared/**",
|
||||||
"files": ["*.ts", "*.tsx"],
|
"@app-config/**",
|
||||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
"@upload-download/**"
|
||||||
"rules": {}
|
],
|
||||||
},
|
"depConstraints": [
|
||||||
{
|
{
|
||||||
"files": ["*.js", "*.jsx"],
|
"sourceTag": "*",
|
||||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
"onlyDependOnLibsWithTags": ["*"]
|
||||||
"rules": {}
|
}
|
||||||
},
|
]
|
||||||
{
|
}
|
||||||
"files": ["*.ts"],
|
]
|
||||||
"rules": {
|
}
|
||||||
"@angular-eslint/component-selector": [
|
},
|
||||||
"error",
|
{
|
||||||
{
|
"files": ["*.ts", "*.tsx"],
|
||||||
"type": "element",
|
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||||
"prefix": "app",
|
"rules": {}
|
||||||
"style": "kebab-case"
|
},
|
||||||
}
|
{
|
||||||
],
|
"files": ["*.js", "*.jsx"],
|
||||||
"@angular-eslint/directive-selector": [
|
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||||
"error",
|
"rules": {}
|
||||||
{
|
},
|
||||||
"type": "attribute",
|
{
|
||||||
"prefix": "app",
|
"files": ["*.ts"],
|
||||||
"style": "camelCase"
|
"rules": {
|
||||||
}
|
"@angular-eslint/component-selector": [
|
||||||
],
|
"error",
|
||||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
{
|
||||||
"@angular-eslint/no-host-metadata-property": "error",
|
"type": "element",
|
||||||
"@angular-eslint/no-input-rename": "error",
|
"prefix": "app",
|
||||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
"style": "kebab-case"
|
||||||
"@angular-eslint/no-output-native": "error",
|
}
|
||||||
"@angular-eslint/no-output-on-prefix": "error",
|
],
|
||||||
"@angular-eslint/no-output-rename": "error",
|
"@angular-eslint/directive-selector": [
|
||||||
"@angular-eslint/no-outputs-metadata-property": "error",
|
"error",
|
||||||
"@angular-eslint/use-lifecycle-interface": "error",
|
{
|
||||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
"type": "attribute",
|
||||||
"@typescript-eslint/consistent-type-definitions": "error",
|
"prefix": "app",
|
||||||
"@typescript-eslint/dot-notation": "off",
|
"style": "camelCase"
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
}
|
||||||
"@typescript-eslint/ban-types": "off",
|
],
|
||||||
"@typescript-eslint/explicit-member-accessibility": [
|
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||||
"warn",
|
"@angular-eslint/no-host-metadata-property": "error",
|
||||||
{
|
"@angular-eslint/no-input-rename": "error",
|
||||||
"accessibility": "no-public"
|
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||||
}
|
"@angular-eslint/no-output-native": "error",
|
||||||
],
|
"@angular-eslint/no-output-on-prefix": "error",
|
||||||
"@typescript-eslint/member-ordering": "error",
|
"@angular-eslint/no-output-rename": "error",
|
||||||
"@typescript-eslint/naming-convention": [
|
"@angular-eslint/no-outputs-metadata-property": "error",
|
||||||
"error",
|
"@angular-eslint/use-lifecycle-interface": "error",
|
||||||
{
|
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||||
"selector": "memberLike",
|
"@typescript-eslint/consistent-type-definitions": "error",
|
||||||
"modifiers": ["readonly"],
|
"@typescript-eslint/dot-notation": "off",
|
||||||
"format": ["UPPER_CASE", "camelCase"]
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
},
|
"@typescript-eslint/ban-types": "off",
|
||||||
{
|
"@typescript-eslint/explicit-member-accessibility": [
|
||||||
"selector": "enumMember",
|
"warn",
|
||||||
"format": ["UPPER_CASE"]
|
{
|
||||||
},
|
"accessibility": "no-public"
|
||||||
{
|
}
|
||||||
"selector": "memberLike",
|
],
|
||||||
"modifiers": ["private"],
|
"@typescript-eslint/member-ordering": "error",
|
||||||
"format": ["camelCase"],
|
"@typescript-eslint/naming-convention": [
|
||||||
"leadingUnderscore": "require"
|
"error",
|
||||||
},
|
{
|
||||||
{
|
"selector": "memberLike",
|
||||||
"selector": "memberLike",
|
"modifiers": ["readonly"],
|
||||||
"modifiers": ["protected"],
|
"format": ["UPPER_CASE", "camelCase"]
|
||||||
"format": ["camelCase"],
|
},
|
||||||
"leadingUnderscore": "require"
|
{
|
||||||
},
|
"selector": "enumMember",
|
||||||
{
|
"format": ["UPPER_CASE"]
|
||||||
"selector": "memberLike",
|
},
|
||||||
"modifiers": ["private", "readonly"],
|
{
|
||||||
"format": ["UPPER_CASE", "camelCase"],
|
"selector": "memberLike",
|
||||||
"leadingUnderscore": "require"
|
"modifiers": ["private"],
|
||||||
}
|
"format": ["camelCase"],
|
||||||
],
|
"leadingUnderscore": "require"
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
},
|
||||||
"@typescript-eslint/no-empty-interface": "error",
|
{
|
||||||
"@typescript-eslint/no-inferrable-types": [
|
"selector": "memberLike",
|
||||||
"error",
|
"modifiers": ["protected"],
|
||||||
{
|
"format": ["camelCase"],
|
||||||
"ignoreParameters": true
|
"leadingUnderscore": "require"
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"@typescript-eslint/no-misused-new": "error",
|
"selector": "memberLike",
|
||||||
"@typescript-eslint/no-non-null-assertion": "error",
|
"modifiers": ["private", "readonly"],
|
||||||
"@typescript-eslint/no-shadow": [
|
"format": ["UPPER_CASE", "camelCase"],
|
||||||
"error",
|
"leadingUnderscore": "require"
|
||||||
{
|
}
|
||||||
"hoist": "all"
|
],
|
||||||
}
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
],
|
"@typescript-eslint/no-empty-interface": "error",
|
||||||
"@typescript-eslint/no-unused-expressions": "error",
|
"@typescript-eslint/no-inferrable-types": [
|
||||||
"@typescript-eslint/prefer-function-type": "error",
|
"error",
|
||||||
"@typescript-eslint/unified-signatures": "error",
|
{
|
||||||
"arrow-body-style": "error",
|
"ignoreParameters": true
|
||||||
"constructor-super": "error",
|
}
|
||||||
"eqeqeq": ["error", "smart"],
|
],
|
||||||
"guard-for-in": "error",
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
"id-blacklist": "off",
|
"@typescript-eslint/no-non-null-assertion": "error",
|
||||||
"id-match": "off",
|
"@typescript-eslint/no-shadow": [
|
||||||
"import/no-deprecated": "warn",
|
"error",
|
||||||
"no-bitwise": "error",
|
{
|
||||||
"no-caller": "error",
|
"hoist": "all"
|
||||||
"no-console": [
|
}
|
||||||
"error",
|
],
|
||||||
{
|
"@typescript-eslint/no-unused-expressions": "error",
|
||||||
"allow": [
|
"@typescript-eslint/prefer-function-type": "error",
|
||||||
"log",
|
"@typescript-eslint/unified-signatures": "error",
|
||||||
"warn",
|
"arrow-body-style": "error",
|
||||||
"dir",
|
"constructor-super": "error",
|
||||||
"timeLog",
|
"eqeqeq": ["error", "smart"],
|
||||||
"assert",
|
"guard-for-in": "error",
|
||||||
"clear",
|
"id-blacklist": "off",
|
||||||
"count",
|
"id-match": "off",
|
||||||
"countReset",
|
"import/no-deprecated": "warn",
|
||||||
"group",
|
"no-bitwise": "error",
|
||||||
"groupEnd",
|
"no-caller": "error",
|
||||||
"table",
|
"no-console": [
|
||||||
"dirxml",
|
"error",
|
||||||
"error",
|
{
|
||||||
"groupCollapsed",
|
"allow": [
|
||||||
"Console",
|
"log",
|
||||||
"profile",
|
"warn",
|
||||||
"profileEnd",
|
"dir",
|
||||||
"timeStamp",
|
"timeLog",
|
||||||
"context"
|
"assert",
|
||||||
]
|
"clear",
|
||||||
}
|
"count",
|
||||||
],
|
"countReset",
|
||||||
"no-debugger": "error",
|
"group",
|
||||||
"no-empty": "off",
|
"groupEnd",
|
||||||
"no-eval": "error",
|
"table",
|
||||||
"no-fallthrough": "error",
|
"dirxml",
|
||||||
"no-new-wrappers": "error",
|
"error",
|
||||||
"no-restricted-imports": ["error", "rxjs/Rx"],
|
"groupCollapsed",
|
||||||
"no-throw-literal": "error",
|
"Console",
|
||||||
"no-undef-init": "error",
|
"profile",
|
||||||
"no-underscore-dangle": "off",
|
"profileEnd",
|
||||||
"no-var": "error",
|
"timeStamp",
|
||||||
"prefer-const": "error",
|
"context"
|
||||||
"radix": "error"
|
]
|
||||||
},
|
}
|
||||||
"plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
],
|
||||||
},
|
"no-debugger": "error",
|
||||||
{
|
"no-empty": "off",
|
||||||
"files": ["*.html"],
|
"no-eval": "error",
|
||||||
"rules": {
|
"no-fallthrough": "error",
|
||||||
"@angular-eslint/template/banana-in-box": "error",
|
"no-new-wrappers": "error",
|
||||||
"@angular-eslint/template/no-negated-async": "error"
|
"no-restricted-imports": ["error", "rxjs/Rx"],
|
||||||
},
|
"no-throw-literal": "error",
|
||||||
"plugins": ["@angular-eslint/eslint-plugin-template"]
|
"no-undef-init": "error",
|
||||||
}
|
"no-underscore-dangle": "off",
|
||||||
]
|
"no-var": "error",
|
||||||
|
"prefer-const": "error",
|
||||||
|
"radix": "error",
|
||||||
|
"max-len": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"code": 100,
|
||||||
|
"tabWidth": 4,
|
||||||
|
"ignorePattern": "^import .*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"@angular-eslint/template/eqeqeq": "error"
|
||||||
|
},
|
||||||
|
"plugins": ["@angular-eslint/eslint-plugin-template"]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,3 +2,5 @@
|
|||||||
|
|
||||||
/dist
|
/dist
|
||||||
/coverage
|
/coverage
|
||||||
|
/node_modules
|
||||||
|
/bamboo-specs
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"printWidth": 160,
|
"printWidth": 100,
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
@ -10,6 +10,12 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"parser": "angular"
|
"parser": "angular"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["*.json"],
|
||||||
|
"options": {
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
386
angular.json
386
angular.json
@ -1,194 +1,200 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"$schema": "node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"cli": {
|
"version": 1,
|
||||||
"defaultCollection": "@nrwl/angular",
|
"cli": {
|
||||||
"analytics": false,
|
"defaultCollection": "@nrwl/angular",
|
||||||
"packageManager": "yarn"
|
"analytics": false,
|
||||||
|
"packageManager": "yarn"
|
||||||
|
},
|
||||||
|
"defaultProject": "red-ui",
|
||||||
|
"schematics": {
|
||||||
|
"@nrwl/angular:application": {
|
||||||
|
"unitTestRunner": "jest",
|
||||||
|
"e2eTestRunner": "cypress"
|
||||||
},
|
},
|
||||||
"defaultProject": "red-ui",
|
"@nrwl/angular:library": {
|
||||||
"schematics": {
|
"unitTestRunner": "jest"
|
||||||
"@nrwl/angular:application": {
|
|
||||||
"unitTestRunner": "jest",
|
|
||||||
"e2eTestRunner": "cypress"
|
|
||||||
},
|
|
||||||
"@nrwl/angular:library": {
|
|
||||||
"unitTestRunner": "jest"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"projects": {
|
|
||||||
"red-ui": {
|
|
||||||
"projectType": "application",
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "apps/red-ui",
|
|
||||||
"sourceRoot": "apps/red-ui/src",
|
|
||||||
"prefix": "redaction",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-angular:browser",
|
|
||||||
"options": {
|
|
||||||
"outputPath": "dist/apps/red-ui",
|
|
||||||
"index": "apps/red-ui/src/index.html",
|
|
||||||
"main": "apps/red-ui/src/main.ts",
|
|
||||||
"polyfills": "apps/red-ui/src/polyfills.ts",
|
|
||||||
"tsConfig": "apps/red-ui/tsconfig.app.json",
|
|
||||||
"aot": true,
|
|
||||||
"assets": [
|
|
||||||
"apps/red-ui/src/favicon.ico",
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "node_modules/@pdftron/webviewer/public/",
|
|
||||||
"output": "/assets/wv-resources/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "apps/red-ui/src/assets/",
|
|
||||||
"output": "/assets/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glob": "**/*",
|
|
||||||
"input": "node_modules/ace-builds/src-min/",
|
|
||||||
"output": "/assets/ace-builds"
|
|
||||||
},
|
|
||||||
"apps/red-ui/src/manifest.webmanifest"
|
|
||||||
],
|
|
||||||
"styles": ["apps/red-ui/src/styles.scss"],
|
|
||||||
"scripts": [
|
|
||||||
"node_modules/@pdftron/webviewer/webviewer.min.js",
|
|
||||||
"node_modules/ace-builds/src-min/ace.js",
|
|
||||||
"node_modules/ace-builds/src-min/mode-java.js",
|
|
||||||
"node_modules/ace-builds/src-min/theme-eclipse.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "apps/red-ui/src/environments/environment.ts",
|
|
||||||
"with": "apps/red-ui/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optimization": true,
|
|
||||||
"outputHashing": "all",
|
|
||||||
"sourceMap": false,
|
|
||||||
"namedChunks": false,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"vendorChunk": false,
|
|
||||||
"buildOptimizer": true,
|
|
||||||
"budgets": [
|
|
||||||
{
|
|
||||||
"type": "initial",
|
|
||||||
"maximumWarning": "2mb",
|
|
||||||
"maximumError": "5mb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "anyComponentStyle",
|
|
||||||
"maximumWarning": "6kb",
|
|
||||||
"maximumError": "10kb"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"serviceWorker": true,
|
|
||||||
"ngswConfigPath": "apps/red-ui/ngsw-config.json"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": ["{options.outputPath}"]
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"builder": "@angular-devkit/build-angular:dev-server",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "red-ui:build"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"browserTarget": "red-ui:build:production"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"extract-i18n": {
|
|
||||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
|
||||||
"options": {
|
|
||||||
"browserTarget": "red-ui:build"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"builder": "@nrwl/linter:eslint",
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["apps/red-ui/src/**/*.ts", "apps/red-ui/src/**/*.html"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@nrwl/jest:jest",
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "apps/red-ui/jest.config.js",
|
|
||||||
"passWithNoTests": true
|
|
||||||
},
|
|
||||||
"outputs": ["coverage/apps/red-ui"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"red-ui-http": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": "libs/red-ui-http",
|
|
||||||
"sourceRoot": "libs/red-ui-http/src",
|
|
||||||
"prefix": "redaction",
|
|
||||||
"architect": {
|
|
||||||
"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": "@nrwl/linter:eslint",
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["libs/red-ui-http/src/**/*.ts", "libs/red-ui-http/src/**/*.html"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@nrwl/jest:jest",
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "libs/red-ui-http/jest.config.js",
|
|
||||||
"passWithNoTests": true
|
|
||||||
},
|
|
||||||
"outputs": ["coverage/libs/red-ui-http"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"red-cache": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": "libs/red-cache",
|
|
||||||
"sourceRoot": "libs/red-cache/src",
|
|
||||||
"prefix": "redaction",
|
|
||||||
"architect": {
|
|
||||||
"lint": {
|
|
||||||
"builder": "@nrwl/linter:eslint",
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["libs/red-cache/src/**/*.ts", "libs/red-cache/src/**/*.html"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@nrwl/jest:jest",
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "libs/red-cache/jest.config.js",
|
|
||||||
"passWithNoTests": true
|
|
||||||
},
|
|
||||||
"outputs": ["coverage/libs/red-cache"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"schematics": {
|
|
||||||
"@schematics/angular:component": {
|
|
||||||
"style": "scss"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"red-ui": {
|
||||||
|
"projectType": "application",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "apps/red-ui",
|
||||||
|
"sourceRoot": "apps/red-ui/src",
|
||||||
|
"prefix": "redaction",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"outputPath": "dist/apps/red-ui",
|
||||||
|
"index": "apps/red-ui/src/index.html",
|
||||||
|
"main": "apps/red-ui/src/main.ts",
|
||||||
|
"polyfills": "apps/red-ui/src/polyfills.ts",
|
||||||
|
"tsConfig": "apps/red-ui/tsconfig.app.json",
|
||||||
|
"assets": [
|
||||||
|
"apps/red-ui/src/favicon.ico",
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/@pdftron/webviewer/public/",
|
||||||
|
"output": "/assets/wv-resources/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "apps/red-ui/src/assets/",
|
||||||
|
"output": "/assets/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"glob": "**/*",
|
||||||
|
"input": "node_modules/ace-builds/src-min/",
|
||||||
|
"output": "/assets/ace-builds"
|
||||||
|
},
|
||||||
|
"apps/red-ui/src/manifest.webmanifest"
|
||||||
|
],
|
||||||
|
"styles": ["apps/red-ui/src/styles.scss"],
|
||||||
|
"scripts": [
|
||||||
|
"node_modules/@pdftron/webviewer/webviewer.min.js",
|
||||||
|
"node_modules/ace-builds/src-min/ace.js",
|
||||||
|
"node_modules/ace-builds/src-min/mode-java.js",
|
||||||
|
"node_modules/ace-builds/src-min/theme-eclipse.js"
|
||||||
|
],
|
||||||
|
"vendorChunk": true,
|
||||||
|
"extractLicenses": false,
|
||||||
|
"buildOptimizer": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"optimization": false,
|
||||||
|
"namedChunks": true
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"fileReplacements": [
|
||||||
|
{
|
||||||
|
"replace": "apps/red-ui/src/environments/environment.ts",
|
||||||
|
"with": "apps/red-ui/src/environments/environment.prod.ts"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"optimization": true,
|
||||||
|
"outputHashing": "all",
|
||||||
|
"sourceMap": false,
|
||||||
|
"namedChunks": false,
|
||||||
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
|
"budgets": [
|
||||||
|
{
|
||||||
|
"type": "initial",
|
||||||
|
"maximumWarning": "2mb",
|
||||||
|
"maximumError": "5mb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "anyComponentStyle",
|
||||||
|
"maximumWarning": "6kb",
|
||||||
|
"maximumError": "10kb"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"serviceWorker": true,
|
||||||
|
"ngswConfigPath": "apps/red-ui/ngsw-config.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": ["{options.outputPath}"]
|
||||||
|
},
|
||||||
|
"serve": {
|
||||||
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "red-ui:build"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"browserTarget": "red-ui:build:production"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extract-i18n": {
|
||||||
|
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||||
|
"options": {
|
||||||
|
"browserTarget": "red-ui:build"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["apps/red-ui/src/**/*.ts", "apps/red-ui/src/**/*.html"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "apps/red-ui/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/apps/red-ui"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"red-ui-http": {
|
||||||
|
"projectType": "library",
|
||||||
|
"root": "libs/red-ui-http",
|
||||||
|
"sourceRoot": "libs/red-ui-http/src",
|
||||||
|
"prefix": "redaction",
|
||||||
|
"architect": {
|
||||||
|
"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": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["libs/red-ui-http/src/**/*.ts", "libs/red-ui-http/src/**/*.html"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "libs/red-ui-http/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/libs/red-ui-http"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"red-cache": {
|
||||||
|
"projectType": "library",
|
||||||
|
"root": "libs/red-cache",
|
||||||
|
"sourceRoot": "libs/red-cache/src",
|
||||||
|
"prefix": "redaction",
|
||||||
|
"architect": {
|
||||||
|
"lint": {
|
||||||
|
"builder": "@nrwl/linter:eslint",
|
||||||
|
"options": {
|
||||||
|
"lintFilePatterns": ["libs/red-cache/src/**/*.ts", "libs/red-cache/src/**/*.html"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@nrwl/jest:jest",
|
||||||
|
"options": {
|
||||||
|
"jestConfig": "libs/red-cache/jest.config.js",
|
||||||
|
"passWithNoTests": true
|
||||||
|
},
|
||||||
|
"outputs": ["coverage/libs/red-cache"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,37 +1,40 @@
|
|||||||
{
|
{
|
||||||
"extends": ["../../.eslintrc.json"],
|
"extends": ["../../.eslintrc.json"],
|
||||||
"ignorePatterns": ["!**/*"],
|
"ignorePatterns": ["!**/*"],
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": ["*.ts"],
|
"files": ["*.ts"],
|
||||||
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
|
"extends": [
|
||||||
"parserOptions": {
|
"plugin:@nrwl/nx/angular",
|
||||||
"project": ["apps/red-ui/tsconfig.*?.json"]
|
"plugin:@angular-eslint/template/process-inline-templates"
|
||||||
},
|
],
|
||||||
"rules": {
|
"parserOptions": {
|
||||||
"@angular-eslint/directive-selector": [
|
"project": ["apps/red-ui/tsconfig.*?.json"]
|
||||||
"error",
|
},
|
||||||
{
|
"rules": {
|
||||||
"type": "attribute",
|
"@angular-eslint/directive-selector": [
|
||||||
"prefix": "redaction",
|
"error",
|
||||||
"style": "camelCase"
|
{
|
||||||
}
|
"type": "attribute",
|
||||||
],
|
"prefix": "redaction",
|
||||||
"@angular-eslint/component-selector": [
|
"style": "camelCase"
|
||||||
"error",
|
}
|
||||||
{
|
],
|
||||||
"type": "element",
|
"@angular-eslint/component-selector": [
|
||||||
"prefix": "redaction",
|
"error",
|
||||||
"style": "kebab-case"
|
{
|
||||||
}
|
"type": "element",
|
||||||
]
|
"prefix": "redaction",
|
||||||
},
|
"style": "kebab-case"
|
||||||
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
}
|
||||||
},
|
]
|
||||||
{
|
},
|
||||||
"files": ["*.html"],
|
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||||
"extends": ["plugin:@nrwl/nx/angular-template"],
|
},
|
||||||
"rules": {}
|
{
|
||||||
}
|
"files": ["*.html"],
|
||||||
]
|
"extends": ["plugin:@nrwl/nx/angular-template"],
|
||||||
|
"rules": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,12 @@ module.exports = {
|
|||||||
globals: {
|
globals: {
|
||||||
'ts-jest': {
|
'ts-jest': {
|
||||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||||
astTransformers: { before: ['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'
|
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
|
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
|
||||||
"index": "/index.html",
|
"index": "/index.html",
|
||||||
"assetGroups": [
|
"assetGroups": [
|
||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"installMode": "prefetch",
|
"installMode": "prefetch",
|
||||||
"resources": {
|
"resources": {
|
||||||
"files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"]
|
"files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "assets",
|
"name": "assets",
|
||||||
"installMode": "lazy",
|
"installMode": "lazy",
|
||||||
"updateMode": "prefetch",
|
"updateMode": "prefetch",
|
||||||
"resources": {
|
"resources": {
|
||||||
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
|
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,8 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: 'main/projects',
|
path: 'main/projects',
|
||||||
component: BaseScreenComponent,
|
component: BaseScreenComponent,
|
||||||
loadChildren: () => import('./modules/projects/projects.module').then((m) => m.ProjectsModule)
|
loadChildren: () =>
|
||||||
|
import('./modules/projects/projects.module').then((m) => m.ProjectsModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'main/downloads',
|
path: 'main/downloads',
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
<router-outlet></router-outlet>
|
<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>
|
||||||
|
|||||||
@ -8,5 +8,8 @@ import { RouterHistoryService } from '@services/router-history.service';
|
|||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor(public appLoadStateService: AppLoadStateService, private readonly _routerHistoryService: RouterHistoryService) {}
|
constructor(
|
||||||
|
public appLoadStateService: AppLoadStateService,
|
||||||
|
private readonly _routerHistoryService: RouterHistoryService
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -47,7 +47,14 @@ function cleanupBaseUrl(baseUrl: string) {
|
|||||||
|
|
||||||
const screens = [BaseScreenComponent, DownloadsListScreenComponent, UserProfileScreenComponent];
|
const screens = [BaseScreenComponent, DownloadsListScreenComponent, UserProfileScreenComponent];
|
||||||
|
|
||||||
const components = [AppComponent, LogoComponent, AuthErrorComponent, ToastComponent, NotificationsComponent, ...screens];
|
const components = [
|
||||||
|
AppComponent,
|
||||||
|
LogoComponent,
|
||||||
|
AuthErrorComponent,
|
||||||
|
ToastComponent,
|
||||||
|
NotificationsComponent,
|
||||||
|
...screens
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [...components],
|
declarations: [...components],
|
||||||
@ -113,7 +120,11 @@ export class AppModule {
|
|||||||
|
|
||||||
private _configureKeyCloakRouteHandling() {
|
private _configureKeyCloakRouteHandling() {
|
||||||
this._route.queryParamMap.subscribe((queryParams) => {
|
this._route.queryParamMap.subscribe((queryParams) => {
|
||||||
if (queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) {
|
if (
|
||||||
|
queryParams.has('code') ||
|
||||||
|
queryParams.has('state') ||
|
||||||
|
queryParams.has('session_state')
|
||||||
|
) {
|
||||||
this._router.navigate([], {
|
this._router.navigate([], {
|
||||||
queryParams: {
|
queryParams: {
|
||||||
state: null,
|
state: null,
|
||||||
|
|||||||
@ -1,8 +1,15 @@
|
|||||||
<section>
|
<section>
|
||||||
<p *ngIf="!configuredAdminName && !configuredAdminUrl" [translate]="'auth-error.heading'" class="heading-xl"></p>
|
<p
|
||||||
|
*ngIf="!configuredAdminName && !configuredAdminUrl"
|
||||||
|
[translate]="'auth-error.heading'"
|
||||||
|
class="heading-xl"
|
||||||
|
></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="configuredAdminName && configuredAdminUrl"
|
*ngIf="configuredAdminName && configuredAdminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }"
|
[innerHTML]="
|
||||||
|
'auth-error.heading-with-name-and-link'
|
||||||
|
| translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }
|
||||||
|
"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<p
|
<p
|
||||||
|
|||||||
@ -11,10 +11,15 @@ export class AuthErrorComponent implements OnInit {
|
|||||||
configuredAdminName: string;
|
configuredAdminName: string;
|
||||||
configuredAdminUrl: string;
|
configuredAdminUrl: string;
|
||||||
|
|
||||||
constructor(private readonly _userService: UserService, private readonly _appConfigService: AppConfigService) {}
|
constructor(
|
||||||
|
private readonly _userService: UserService,
|
||||||
|
private readonly _appConfigService: AppConfigService
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.configuredAdminName = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_NAME);
|
this.configuredAdminName = this._appConfigService.getConfig(
|
||||||
|
AppConfigKey.ADMIN_CONTACT_NAME
|
||||||
|
);
|
||||||
this.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
|
this.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,20 +6,36 @@
|
|||||||
<mat-icon svgIcon="red:menu"></mat-icon>
|
<mat-icon svgIcon="red:menu"></mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #menuNav="matMenu">
|
<mat-menu #menuNav="matMenu">
|
||||||
<button mat-menu-item routerLink="/main/projects" translate="top-bar.navigation-items.projects"></button>
|
<button
|
||||||
<button *ngIf="appStateService.activeProject" [routerLink]="'/main/projects/' + appStateService.activeProjectId" mat-menu-item>
|
mat-menu-item
|
||||||
|
routerLink="/main/projects"
|
||||||
|
translate="top-bar.navigation-items.projects"
|
||||||
|
></button>
|
||||||
|
<button
|
||||||
|
*ngIf="appStateService.activeProject"
|
||||||
|
[routerLink]="'/main/projects/' + appStateService.activeProjectId"
|
||||||
|
mat-menu-item
|
||||||
|
>
|
||||||
{{ appStateService.activeProject.project.projectName }}
|
{{ appStateService.activeProject.project.projectName }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="appStateService.activeFile"
|
*ngIf="appStateService.activeFile"
|
||||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
|
[routerLink]="
|
||||||
|
'/main/projects/' +
|
||||||
|
appStateService.activeProjectId +
|
||||||
|
'/file/' +
|
||||||
|
appStateService.activeFile.fileId
|
||||||
|
"
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
>
|
>
|
||||||
{{ appStateService.activeFile.filename }}
|
{{ appStateService.activeFile.filename }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="permissionsService.isUser()" class="menu flex-2 visible-lg breadcrumbs-container">
|
<div
|
||||||
|
*ngIf="permissionsService.isUser()"
|
||||||
|
class="menu flex-2 visible-lg breadcrumbs-container"
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
*ngIf="projectsView"
|
*ngIf="projectsView"
|
||||||
[routerLinkActiveOptions]="{ exact: true }"
|
[routerLinkActiveOptions]="{ exact: true }"
|
||||||
@ -33,8 +49,15 @@
|
|||||||
{{ 'top-bar.navigation-items.back' | translate }}
|
{{ 'top-bar.navigation-items.back' | translate }}
|
||||||
</a>
|
</a>
|
||||||
<ng-container *ngIf="projectsView">
|
<ng-container *ngIf="projectsView">
|
||||||
<mat-icon *ngIf="!appStateService.activeProject" class="primary" svgIcon="red:arrow-down"></mat-icon>
|
<mat-icon
|
||||||
<mat-icon *ngIf="appStateService.activeProject" svgIcon="red:arrow-right"></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
|
<a
|
||||||
*ngIf="appStateService.activeProject"
|
*ngIf="appStateService.activeProject"
|
||||||
[routerLinkActiveOptions]="{ exact: true }"
|
[routerLinkActiveOptions]="{ exact: true }"
|
||||||
@ -47,7 +70,12 @@
|
|||||||
<mat-icon *ngIf="appStateService.activeFile" svgIcon="red:arrow-right"></mat-icon>
|
<mat-icon *ngIf="appStateService.activeFile" svgIcon="red:arrow-right"></mat-icon>
|
||||||
<a
|
<a
|
||||||
*ngIf="appStateService.activeFile"
|
*ngIf="appStateService.activeFile"
|
||||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
|
[routerLink]="
|
||||||
|
'/main/projects/' +
|
||||||
|
appStateService.activeProjectId +
|
||||||
|
'/file/' +
|
||||||
|
appStateService.activeFile.fileId
|
||||||
|
"
|
||||||
class="breadcrumb"
|
class="breadcrumb"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
>
|
>
|
||||||
@ -60,13 +88,28 @@
|
|||||||
<redaction-logo></redaction-logo>
|
<redaction-logo></redaction-logo>
|
||||||
</redaction-hidden-action>
|
</redaction-hidden-action>
|
||||||
<div class="app-name">{{ titleService.getTitle() }}</div>
|
<div class="app-name">{{ titleService.getTitle() }}</div>
|
||||||
<span *ngIf="userPreferenceService.areDevFeaturesEnabled" class="dev-mode" translate="dev-mode"></span>
|
<span
|
||||||
|
*ngIf="userPreferenceService.areDevFeaturesEnabled"
|
||||||
|
class="dev-mode"
|
||||||
|
translate="dev-mode"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu right flex-2">
|
<div class="menu right flex-2">
|
||||||
<redaction-notifications *ngIf="userPreferenceService.areDevFeaturesEnabled" class="mr-8"></redaction-notifications>
|
<redaction-notifications
|
||||||
<redaction-user-button [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot" [user]="user"></redaction-user-button>
|
*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">
|
<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
|
||||||
|
[routerLink]="'/main/my-profile'"
|
||||||
|
mat-menu-item
|
||||||
|
translate="top-bar.navigation-items.my-account.children.my-profile"
|
||||||
|
></button>
|
||||||
<button
|
<button
|
||||||
(click)="appStateService.reset()"
|
(click)="appStateService.reset()"
|
||||||
*ngIf="permissionsService.isManager() || permissionsService.isUserAdmin()"
|
*ngIf="permissionsService.isManager() || permissionsService.isUserAdmin()"
|
||||||
|
|||||||
@ -14,26 +14,49 @@
|
|||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="header-item">
|
<div class="header-item">
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'downloads-list.table-header.title' | translate: { length: fileDownloadService.downloads.length } }}
|
{{
|
||||||
|
'downloads-list.table-header.title'
|
||||||
|
| translate: { length: fileDownloadService.downloads.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
|
<div [class.no-data]="noData" class="table-header" redactionSyncWidth="table-item">
|
||||||
<redaction-table-col-name label="downloads-list.table-col-names.name"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
<redaction-table-col-name label="downloads-list.table-col-names.size"></redaction-table-col-name>
|
label="downloads-list.table-col-names.name"
|
||||||
<redaction-table-col-name label="downloads-list.table-col-names.date"></redaction-table-col-name>
|
></redaction-table-col-name>
|
||||||
<redaction-table-col-name label="downloads-list.table-col-names.status"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
label="downloads-list.table-col-names.size"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
<redaction-table-col-name
|
||||||
|
label="downloads-list.table-col-names.date"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
<redaction-table-col-name
|
||||||
|
label="downloads-list.table-col-names.status"
|
||||||
|
></redaction-table-col-name>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="noData" icon="red:download" screen="downloads-list"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="noData"
|
||||||
|
icon="red:download"
|
||||||
|
screen="downloads-list"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<!-- Table lines -->
|
<!-- Table lines -->
|
||||||
<div *cdkVirtualFor="let download of fileDownloadService.downloads" class="table-item">
|
<div
|
||||||
|
*cdkVirtualFor="let download of fileDownloadService.downloads"
|
||||||
|
class="table-item"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div [class.no-bold]="download.lastDownload" class="table-item-title heading">{{ download.filename }}</div>
|
<div
|
||||||
|
[class.no-bold]="download.lastDownload"
|
||||||
|
class="table-item-title heading"
|
||||||
|
>
|
||||||
|
{{ download.filename }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="small-label">
|
<div class="small-label">
|
||||||
@ -61,7 +84,12 @@
|
|||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button (action)="deleteItem(download)" icon="red:trash" tooltip="downloads-list.actions.delete" type="dark-bg">
|
<redaction-circle-button
|
||||||
|
(action)="deleteItem(download)"
|
||||||
|
icon="red:trash"
|
||||||
|
tooltip="downloads-list.actions.delete"
|
||||||
|
type="dark-bg"
|
||||||
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<mat-spinner *ngIf="download.inProgress" diameter="15"></mat-spinner>
|
<mat-spinner *ngIf="download.inProgress" diameter="15"></mat-spinner>
|
||||||
|
|||||||
@ -9,7 +9,10 @@ import { DownloadControllerService } from '@redaction/red-ui-http';
|
|||||||
styleUrls: ['./downloads-list-screen.component.scss']
|
styleUrls: ['./downloads-list-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class DownloadsListScreenComponent implements OnInit {
|
export class DownloadsListScreenComponent implements OnInit {
|
||||||
constructor(readonly fileDownloadService: FileDownloadService, private readonly _downloadControllerService: DownloadControllerService) {}
|
constructor(
|
||||||
|
readonly fileDownloadService: FileDownloadService,
|
||||||
|
private readonly _downloadControllerService: DownloadControllerService
|
||||||
|
) {}
|
||||||
|
|
||||||
get noData(): boolean {
|
get noData(): boolean {
|
||||||
return this.fileDownloadService.downloads.length === 0;
|
return this.fileDownloadService.downloads.length === 0;
|
||||||
|
|||||||
@ -1,5 +1,14 @@
|
|||||||
<redaction-circle-button [matMenuTriggerFor]="overlay" [showDot]="hasUnread" icon="red:notification"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
<mat-menu #overlay="matMenu" backdropClass="notifications-backdrop" class="notifications-menu" xPosition="before">
|
[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 *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
|
||||||
<div class="all-caps-label">{{ day(group) }}</div>
|
<div class="all-caps-label">{{ day(group) }}</div>
|
||||||
<div
|
<div
|
||||||
@ -15,7 +24,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
(click)="toggleRead(notification, $event)"
|
(click)="toggleRead(notification, $event)"
|
||||||
[matTooltip]="(notification.read ? 'notifications.mark-unread' : 'notifications.mark-read') | translate"
|
[matTooltip]="
|
||||||
|
(notification.read ? 'notifications.mark-unread' : 'notifications.mark-read')
|
||||||
|
| translate
|
||||||
|
"
|
||||||
class="dot"
|
class="dot"
|
||||||
matTooltipPosition="before"
|
matTooltipPosition="before"
|
||||||
></div>
|
></div>
|
||||||
|
|||||||
@ -2,8 +2,20 @@
|
|||||||
<div *ngIf="title" [attr.aria-label]="title" [class]="options.titleClass">
|
<div *ngIf="title" [attr.aria-label]="title" [class]="options.titleClass">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="message && options.enableHtml" [class]="options.messageClass" [innerHTML]="message" aria-live="polite" role="alert"></div>
|
<div
|
||||||
<div *ngIf="message && !options.enableHtml" [attr.aria-label]="message" [class]="options.messageClass" aria-live="polite" role="alert">
|
*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 }}
|
{{ message }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,10 @@ import { Toast, ToastPackage, ToastrService } from 'ngx-toastr';
|
|||||||
styleUrls: ['./toast.component.scss']
|
styleUrls: ['./toast.component.scss']
|
||||||
})
|
})
|
||||||
export class ToastComponent extends Toast {
|
export class ToastComponent extends Toast {
|
||||||
constructor(protected readonly _toastrService: ToastrService, readonly toastPackage: ToastPackage) {
|
constructor(
|
||||||
|
protected readonly _toastrService: ToastrService,
|
||||||
|
readonly toastPackage: ToastPackage
|
||||||
|
) {
|
||||||
super(_toastrService, toastPackage);
|
super(_toastrService, toastPackage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,17 +23,29 @@
|
|||||||
<input formControlName="lastName" name="lastName" type="text" />
|
<input formControlName="lastName" name="lastName" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
|
<label
|
||||||
|
[translate]="
|
||||||
|
'top-bar.navigation-items.my-account.children.language.label'
|
||||||
|
"
|
||||||
|
></label>
|
||||||
<mat-select formControlName="language">
|
<mat-select formControlName="language">
|
||||||
<mat-option *ngFor="let language of languages" [value]="language">
|
<mat-option *ngFor="let language of languages" [value]="language">
|
||||||
{{ 'top-bar.navigation-items.my-account.children.language.' + language | translate }}
|
{{
|
||||||
|
'top-bar.navigation-items.my-account.children.language.' +
|
||||||
|
language | translate
|
||||||
|
}}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="formGroup.invalid || !(profileChanged || languageChanged)" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="formGroup.invalid || !(profileChanged || languageChanged)"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'user-profile.actions.save' | translate }}
|
{{ 'user-profile.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -42,4 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -30,8 +30,10 @@ export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate
|
|||||||
// if there are no pending changes, just allow deactivation; else confirm first
|
// if there are no pending changes, just allow deactivation; else confirm first
|
||||||
return !component.hasChanges
|
return !component.hasChanges
|
||||||
? true
|
? true
|
||||||
: // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
|
: // NOTE: this warning message will only be shown when navigating elsewhere
|
||||||
// when navigating away from your angular app, the browser will show a generic warning message
|
// 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
|
// see http://stackoverflow.com/a/42207299/7307355
|
||||||
confirm(this._translateService.instant('pending-changes-guard'));
|
confirm(this._translateService.instant('pending-changes-guard'));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,10 @@
|
|||||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
|
import {
|
||||||
|
ActivatedRouteSnapshot,
|
||||||
|
CanActivate,
|
||||||
|
Router,
|
||||||
|
RouterStateSnapshot,
|
||||||
|
UrlTree
|
||||||
|
} from '@angular/router';
|
||||||
import { Injectable, Injector } from '@angular/core';
|
import { Injectable, Injector } from '@angular/core';
|
||||||
import { from, of } from 'rxjs';
|
import { from, of } from 'rxjs';
|
||||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||||
@ -7,7 +13,11 @@ import { AppLoadStateService } from '@services/app-load-state.service';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class CompositeRouteGuard implements CanActivate {
|
export class CompositeRouteGuard implements CanActivate {
|
||||||
constructor(protected readonly _router: Router, protected readonly _injector: Injector, private readonly _appLoadStateService: AppLoadStateService) {}
|
constructor(
|
||||||
|
protected readonly _router: Router,
|
||||||
|
protected readonly _injector: Injector,
|
||||||
|
private readonly _appLoadStateService: AppLoadStateService
|
||||||
|
) {}
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
this._appLoadStateService.pushLoadingEvent(true);
|
this._appLoadStateService.pushLoadingEvent(true);
|
||||||
@ -21,7 +31,10 @@ export class CompositeRouteGuard implements CanActivate {
|
|||||||
if (canActivateResult instanceof Promise) {
|
if (canActivateResult instanceof Promise) {
|
||||||
canActivateResult = from(canActivateResult);
|
canActivateResult = from(canActivateResult);
|
||||||
}
|
}
|
||||||
if (typeof canActivateResult === 'boolean' || canActivateResult instanceof UrlTree) {
|
if (
|
||||||
|
typeof canActivateResult === 'boolean' ||
|
||||||
|
canActivateResult instanceof UrlTree
|
||||||
|
) {
|
||||||
canActivateResult = of(canActivateResult);
|
canActivateResult = of(canActivateResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,22 +30,34 @@ export class AnnotationPermissions {
|
|||||||
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
||||||
|
|
||||||
permissions.canUndo =
|
permissions.canUndo =
|
||||||
annotation.isUndoableSuperType && (annotation.userId === user.id || (annotation.userId && isApprover && !annotation.isSuggestion));
|
annotation.isUndoableSuperType &&
|
||||||
|
(annotation.userId === user.id ||
|
||||||
|
(annotation.userId && isApprover && !annotation.isSuggestion));
|
||||||
|
|
||||||
permissions.canForceRedaction = annotation.isSkipped && !permissions.canUndo;
|
permissions.canForceRedaction = annotation.isSkipped && !permissions.canUndo;
|
||||||
|
|
||||||
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
||||||
|
|
||||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive && !annotation.force;
|
permissions.canMarkAsFalsePositive =
|
||||||
permissions.canMarkTextOnlyAsFalsePositive = annotation.canBeMarkedAsFalsePositiveWithTextOnly && !annotation.force;
|
annotation.canBeMarkedAsFalsePositive && !annotation.force;
|
||||||
|
permissions.canMarkTextOnlyAsFalsePositive =
|
||||||
|
annotation.canBeMarkedAsFalsePositiveWithTextOnly && !annotation.force;
|
||||||
|
|
||||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted && !annotation.force;
|
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted && !annotation.force;
|
||||||
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
||||||
annotation.isRedacted && !annotation.isManualRedaction && annotation.isModifyDictionary && !annotation.force;
|
annotation.isRedacted &&
|
||||||
|
!annotation.isManualRedaction &&
|
||||||
|
annotation.isModifyDictionary &&
|
||||||
|
!annotation.force;
|
||||||
|
|
||||||
permissions.canAcceptSuggestion = isApprover && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
permissions.canAcceptSuggestion =
|
||||||
|
isApprover && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
||||||
permissions.canRejectSuggestion =
|
permissions.canRejectSuggestion =
|
||||||
isApprover && (annotation.isSuggestion || (annotation.isReadyForAnalysis && !permissions.canUndo && annotation.superType !== 'pending-analysis'));
|
isApprover &&
|
||||||
|
(annotation.isSuggestion ||
|
||||||
|
(annotation.isReadyForAnalysis &&
|
||||||
|
!permissions.canUndo &&
|
||||||
|
annotation.superType !== 'pending-analysis'));
|
||||||
|
|
||||||
return permissions;
|
return permissions;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,15 +79,28 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get canBeMarkedAsFalsePositive() {
|
get canBeMarkedAsFalsePositive() {
|
||||||
return (this.isRecommendation || this.superType === 'redaction') && (this.hasTextAfter || this.hasTextBefore) && !this.isImage;
|
return (
|
||||||
|
(this.isRecommendation || this.superType === 'redaction') &&
|
||||||
|
(this.hasTextAfter || this.hasTextBefore) &&
|
||||||
|
!this.isImage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canBeMarkedAsFalsePositiveWithTextOnly() {
|
get canBeMarkedAsFalsePositiveWithTextOnly() {
|
||||||
return !this.canBeMarkedAsFalsePositive && (this.isRecommendation || this.superType === 'redaction') && !this.isImage;
|
return (
|
||||||
|
!this.canBeMarkedAsFalsePositive &&
|
||||||
|
(this.isRecommendation || this.superType === 'redaction') &&
|
||||||
|
!this.isImage
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuperTypeBasedColor() {
|
get isSuperTypeBasedColor() {
|
||||||
return this.isSkipped || this.isSuggestion || this.isReadyForAnalysis || this.isDeclinedSuggestion;
|
return (
|
||||||
|
this.isSkipped ||
|
||||||
|
this.isSuggestion ||
|
||||||
|
this.isReadyForAnalysis ||
|
||||||
|
this.isDeclinedSuggestion
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSkipped() {
|
get isSkipped() {
|
||||||
@ -104,7 +117,10 @@ export class AnnotationWrapper {
|
|||||||
|
|
||||||
get isFalsePositive() {
|
get isFalsePositive() {
|
||||||
return (
|
return (
|
||||||
this.dictionary?.toLowerCase() === 'false_positive' && (this.superType === 'skipped' || this.superType === 'hint' || this.superType === 'redaction')
|
this.dictionary?.toLowerCase() === 'false_positive' &&
|
||||||
|
(this.superType === 'skipped' ||
|
||||||
|
this.superType === 'hint' ||
|
||||||
|
this.superType === 'redaction')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,11 +158,18 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestionAdd() {
|
get isSuggestionAdd() {
|
||||||
return this.superType === 'suggestion-add' || this.superType === 'suggestion-add-dictionary' || this.superType === 'suggestion-force-redaction';
|
return (
|
||||||
|
this.superType === 'suggestion-add' ||
|
||||||
|
this.superType === 'suggestion-add-dictionary' ||
|
||||||
|
this.superType === 'suggestion-force-redaction'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestionRemove() {
|
get isSuggestionRemove() {
|
||||||
return this.superType === 'suggestion-remove' || this.superType === 'suggestion-remove-dictionary';
|
return (
|
||||||
|
this.superType === 'suggestion-remove' ||
|
||||||
|
this.superType === 'suggestion-remove-dictionary'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isModifyDictionary() {
|
get isModifyDictionary() {
|
||||||
@ -154,7 +177,10 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isConvertedRecommendation() {
|
get isConvertedRecommendation() {
|
||||||
return this.isRecommendation && (this.superType === 'suggestion-add-dictionary' || this.superType === 'add-dictionary');
|
return (
|
||||||
|
this.isRecommendation &&
|
||||||
|
(this.superType === 'suggestion-add-dictionary' || this.superType === 'add-dictionary')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isRecommendation() {
|
get isRecommendation() {
|
||||||
@ -203,13 +229,21 @@ export class AnnotationWrapper {
|
|||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) {
|
private static _handleRecommendations(
|
||||||
|
annotationWrapper: AnnotationWrapper,
|
||||||
|
redactionLogEntry: RedactionLogEntryWrapper
|
||||||
|
) {
|
||||||
if (annotationWrapper.superType === 'recommendation') {
|
if (annotationWrapper.superType === 'recommendation') {
|
||||||
annotationWrapper.recommendationType = redactionLogEntry.type.substr('recommendation_'.length);
|
annotationWrapper.recommendationType = redactionLogEntry.type.substr(
|
||||||
|
'recommendation_'.length
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _setSuperType(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
private static _setSuperType(
|
||||||
|
annotationWrapper: AnnotationWrapper,
|
||||||
|
redactionLogEntryWrapper: RedactionLogEntryWrapper
|
||||||
|
) {
|
||||||
if (redactionLogEntryWrapper.recommendation) {
|
if (redactionLogEntryWrapper.recommendation) {
|
||||||
if (redactionLogEntryWrapper.redacted) {
|
if (redactionLogEntryWrapper.redacted) {
|
||||||
annotationWrapper.superType = 'recommendation';
|
annotationWrapper.superType = 'recommendation';
|
||||||
@ -235,23 +269,40 @@ export class AnnotationWrapper {
|
|||||||
} else {
|
} else {
|
||||||
if (redactionLogEntryWrapper.redacted) {
|
if (redactionLogEntryWrapper.redacted) {
|
||||||
if (redactionLogEntryWrapper.force) {
|
if (redactionLogEntryWrapper.force) {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'redaction';
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.status === 'REQUESTED'
|
||||||
|
? 'suggestion-add'
|
||||||
|
: 'redaction';
|
||||||
} else if (redactionLogEntryWrapper.type === 'manual') {
|
} else if (redactionLogEntryWrapper.type === 'manual') {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'manual-redaction';
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.status === 'REQUESTED'
|
||||||
|
? 'suggestion-add'
|
||||||
|
: 'manual-redaction';
|
||||||
} else {
|
} else {
|
||||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||||
annotationWrapper.superType =
|
annotationWrapper.superType =
|
||||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add-dictionary' : 'suggestion-remove-dictionary';
|
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||||
|
? 'suggestion-add-dictionary'
|
||||||
|
: 'suggestion-remove-dictionary';
|
||||||
} else {
|
} else {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add' : 'suggestion-remove';
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||||
|
? 'suggestion-add'
|
||||||
|
: 'suggestion-remove';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'add-dictionary' : 'remove-dictionary';
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||||
|
? 'add-dictionary'
|
||||||
|
: 'remove-dictionary';
|
||||||
} else {
|
} else {
|
||||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'manual-redaction' : 'remove-only-here';
|
annotationWrapper.superType =
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||||
|
? 'manual-redaction'
|
||||||
|
: 'remove-only-here';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,14 +310,24 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!annotationWrapper.superType) {
|
if (!annotationWrapper.superType) {
|
||||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
annotationWrapper.superType = annotationWrapper.redaction
|
||||||
|
? 'redaction'
|
||||||
|
: annotationWrapper.hint
|
||||||
|
? 'hint'
|
||||||
|
: 'skipped';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _handleSkippedState(annotationWrapper: AnnotationWrapper, redactionLogEntryWrapper: RedactionLogEntryWrapper) {
|
private static _handleSkippedState(
|
||||||
|
annotationWrapper: AnnotationWrapper,
|
||||||
|
redactionLogEntryWrapper: RedactionLogEntryWrapper
|
||||||
|
) {
|
||||||
if (annotationWrapper.superType === 'skipped') {
|
if (annotationWrapper.superType === 'skipped') {
|
||||||
if (!annotationWrapper.userId) {
|
if (!annotationWrapper.userId) {
|
||||||
if (redactionLogEntryWrapper.manualRedactionType === 'REMOVE' || redactionLogEntryWrapper.manualRedactionType === 'UNDO') {
|
if (
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'REMOVE' ||
|
||||||
|
redactionLogEntryWrapper.manualRedactionType === 'UNDO'
|
||||||
|
) {
|
||||||
annotationWrapper.superType = 'pending-analysis';
|
annotationWrapper.superType = 'pending-analysis';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -283,7 +344,10 @@ export class AnnotationWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
private static _createContent(
|
||||||
|
annotationWrapper: AnnotationWrapper,
|
||||||
|
entry: RedactionLogEntryWrapper
|
||||||
|
) {
|
||||||
let content = '';
|
let content = '';
|
||||||
if (entry.matchedRule) {
|
if (entry.matchedRule) {
|
||||||
content += 'Rule ' + entry.matchedRule + ' matched \n\n';
|
content += 'Rule ' + entry.matchedRule + ' matched \n\n';
|
||||||
|
|||||||
@ -33,7 +33,12 @@ export class FileDataModel {
|
|||||||
return this.redactionLog.redactionLogEntry;
|
return this.redactionLog.redactionLogEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnnotations(dictionaryData: { [p: string]: TypeValue }, currentUser: UserWrapper, viewMode: ViewMode, areDevFeaturesEnabled: boolean): AnnotationData {
|
getAnnotations(
|
||||||
|
dictionaryData: { [p: string]: TypeValue },
|
||||||
|
currentUser: UserWrapper,
|
||||||
|
viewMode: ViewMode,
|
||||||
|
areDevFeaturesEnabled: boolean
|
||||||
|
): AnnotationData {
|
||||||
const entries: RedactionLogEntryWrapper[] = this._convertData(dictionaryData);
|
const entries: RedactionLogEntryWrapper[] = this._convertData(dictionaryData);
|
||||||
let allAnnotations = entries.map((entry) => AnnotationWrapper.fromData(entry));
|
let allAnnotations = entries.map((entry) => AnnotationWrapper.fromData(entry));
|
||||||
|
|
||||||
@ -70,11 +75,14 @@ export class FileDataModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
||||||
|
actionPendingReanalysis: false
|
||||||
|
};
|
||||||
|
|
||||||
Object.assign(redactionLogEntryWrapper, changeLogEntry);
|
Object.assign(redactionLogEntryWrapper, changeLogEntry);
|
||||||
|
|
||||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
redactionLogEntryWrapper.comments =
|
||||||
|
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||||
redactionLogEntryWrapper.isChangeLogEntry = true;
|
redactionLogEntryWrapper.isChangeLogEntry = true;
|
||||||
redactionLogEntryWrapper.changeLogType = changeLogEntry.changeType;
|
redactionLogEntryWrapper.changeLogType = changeLogEntry.changeType;
|
||||||
redactionLogEntryWrapper.id = 'changed-log-removed-' + redactionLogEntryWrapper.id;
|
redactionLogEntryWrapper.id = 'changed-log-removed-' + redactionLogEntryWrapper.id;
|
||||||
@ -88,12 +96,17 @@ export class FileDataModel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingChangeLogEntry = this.redactionChangeLog?.redactionLogEntry?.find((rle) => rle.id === redactionLogEntry.id);
|
const existingChangeLogEntry = this.redactionChangeLog?.redactionLogEntry?.find(
|
||||||
|
(rle) => rle.id === redactionLogEntry.id
|
||||||
|
);
|
||||||
|
|
||||||
// copy the redactionLog Entry
|
// copy the redactionLog Entry
|
||||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
|
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
||||||
|
actionPendingReanalysis: false
|
||||||
|
};
|
||||||
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
redactionLogEntryWrapper.comments =
|
||||||
|
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||||
redactionLogEntryWrapper.isChangeLogEntry = !!existingChangeLogEntry;
|
redactionLogEntryWrapper.isChangeLogEntry = !!existingChangeLogEntry;
|
||||||
redactionLogEntryWrapper.changeLogType = 'ADDED';
|
redactionLogEntryWrapper.changeLogType = 'ADDED';
|
||||||
result.push(redactionLogEntryWrapper);
|
result.push(redactionLogEntryWrapper);
|
||||||
@ -117,7 +130,10 @@ export class FileDataModel {
|
|||||||
relevantRedactionLogEntry.force = true;
|
relevantRedactionLogEntry.force = true;
|
||||||
|
|
||||||
// if statuses differ
|
// if statuses differ
|
||||||
if (!forceRedaction.processedDate || forceRedaction.status !== relevantRedactionLogEntry.status) {
|
if (
|
||||||
|
!forceRedaction.processedDate ||
|
||||||
|
forceRedaction.status !== relevantRedactionLogEntry.status
|
||||||
|
) {
|
||||||
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
||||||
relevantRedactionLogEntry.status = forceRedaction.status;
|
relevantRedactionLogEntry.status = forceRedaction.status;
|
||||||
}
|
}
|
||||||
@ -152,7 +168,8 @@ export class FileDataModel {
|
|||||||
relevantRedactionLogEntry.status = manual.status;
|
relevantRedactionLogEntry.status = manual.status;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// dictionary modifying requests that have been processed already updated the dictionary and should not be drawn
|
// dictionary modifying requests that have been processed already updated
|
||||||
|
// the dictionary and should not be drawn
|
||||||
if (manual.addToDictionary && this._hasAlreadyBeenProcessed(manual)) {
|
if (manual.addToDictionary && this._hasAlreadyBeenProcessed(manual)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -175,9 +192,11 @@ export class FileDataModel {
|
|||||||
redactionLogEntryWrapper.hint = dictionary.hint;
|
redactionLogEntryWrapper.hint = dictionary.hint;
|
||||||
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
||||||
redactionLogEntryWrapper.manual = true;
|
redactionLogEntryWrapper.manual = true;
|
||||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
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
|
// cleanup reason if the reason is another annotationId
|
||||||
|
// it is not needed for drawing
|
||||||
redactionLogEntryWrapper.reason = null;
|
redactionLogEntryWrapper.reason = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,14 +231,18 @@ export class FileDataModel {
|
|||||||
result.forEach((redactionLogEntry) => {
|
result.forEach((redactionLogEntry) => {
|
||||||
if (redactionLogEntry.manual) {
|
if (redactionLogEntry.manual) {
|
||||||
if (redactionLogEntry.manualRedactionType === 'ADD') {
|
if (redactionLogEntry.manualRedactionType === 'ADD') {
|
||||||
const foundManualEntry = this.manualRedactions.entriesToAdd.find((me) => me.id === redactionLogEntry.id);
|
const foundManualEntry = this.manualRedactions.entriesToAdd.find(
|
||||||
|
(me) => me.id === redactionLogEntry.id
|
||||||
|
);
|
||||||
// ADD has been undone - not yet processed
|
// ADD has been undone - not yet processed
|
||||||
if (!foundManualEntry) {
|
if (!foundManualEntry) {
|
||||||
redactionLogEntry.hidden = true;
|
redactionLogEntry.hidden = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (redactionLogEntry.manualRedactionType === 'REMOVE') {
|
if (redactionLogEntry.manualRedactionType === 'REMOVE') {
|
||||||
const foundManualEntry = this.manualRedactions.idsToRemove.find((me) => me.id === redactionLogEntry.id);
|
const foundManualEntry = this.manualRedactions.idsToRemove.find(
|
||||||
|
(me) => me.id === redactionLogEntry.id
|
||||||
|
);
|
||||||
// REMOVE has been undone - not yet processed
|
// REMOVE has been undone - not yet processed
|
||||||
if (!foundManualEntry) {
|
if (!foundManualEntry) {
|
||||||
redactionLogEntry.manual = false;
|
redactionLogEntry.manual = false;
|
||||||
@ -236,6 +259,9 @@ export class FileDataModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _hasAlreadyBeenProcessed(entry: ManualRedactionEntry | IdRemoval): boolean {
|
private _hasAlreadyBeenProcessed(entry: ManualRedactionEntry | IdRemoval): boolean {
|
||||||
return !entry.processedDate ? false : new Date(entry.processedDate).getTime() < new Date(this.fileStatus.lastProcessed).getTime();
|
return !entry.processedDate
|
||||||
|
? false
|
||||||
|
: new Date(entry.processedDate).getTime() <
|
||||||
|
new Date(this.fileStatus.lastProcessed).getTime();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,18 @@ export class FileStatusWrapper {
|
|||||||
primaryAttribute: string;
|
primaryAttribute: string;
|
||||||
searchField: string;
|
searchField: string;
|
||||||
|
|
||||||
constructor(public fileStatus: FileStatus, public reviewerName: string, public ruleSetId: string, fileAttributesConfig?: FileAttributesConfig) {
|
constructor(
|
||||||
|
public fileStatus: FileStatus,
|
||||||
|
public reviewerName: string,
|
||||||
|
public ruleSetId: string,
|
||||||
|
fileAttributesConfig?: FileAttributesConfig
|
||||||
|
) {
|
||||||
this.searchField = fileStatus.filename;
|
this.searchField = fileStatus.filename;
|
||||||
|
|
||||||
if (fileAttributesConfig) {
|
if (fileAttributesConfig) {
|
||||||
const primary = fileAttributesConfig.fileAttributeConfigs?.find((c) => c.primaryAttribute);
|
const primary = fileAttributesConfig.fileAttributeConfigs?.find(
|
||||||
|
(c) => c.primaryAttribute
|
||||||
|
);
|
||||||
if (primary && fileStatus.fileAttributes?.attributeIdToValue) {
|
if (primary && fileStatus.fileAttributes?.attributeIdToValue) {
|
||||||
this.primaryAttribute = fileStatus.fileAttributes?.attributeIdToValue[primary.id];
|
this.primaryAttribute = fileStatus.fileAttributes?.attributeIdToValue[primary.id];
|
||||||
this.searchField += ' ' + this.primaryAttribute;
|
this.searchField += ' ' + this.primaryAttribute;
|
||||||
@ -125,7 +132,9 @@ export class FileStatusWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get status() {
|
get status() {
|
||||||
return this.fileStatus.status === 'REPROCESS' || this.fileStatus.status === 'FULLREPROCESS' ? 'PROCESSING' : this.fileStatus.status;
|
return this.fileStatus.status === 'REPROCESS' || this.fileStatus.status === 'FULLREPROCESS'
|
||||||
|
? 'PROCESSING'
|
||||||
|
: this.fileStatus.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
get numberOfPages() {
|
get numberOfPages() {
|
||||||
@ -195,6 +204,8 @@ export class FileStatusWrapper {
|
|||||||
get newestDate() {
|
get newestDate() {
|
||||||
const uploadedDate = new Date(this.fileStatus.lastUploaded);
|
const uploadedDate = new Date(this.fileStatus.lastUploaded);
|
||||||
const updatedDate = new Date(this.fileStatus.lastUpdated);
|
const updatedDate = new Date(this.fileStatus.lastUpdated);
|
||||||
return updatedDate && updatedDate.getTime() > uploadedDate.getTime() ? updatedDate : uploadedDate;
|
return updatedDate && updatedDate.getTime() > uploadedDate.getTime()
|
||||||
|
? updatedDate
|
||||||
|
: uploadedDate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,16 @@ export class ManualAnnotationResponse {
|
|||||||
annotationId;
|
annotationId;
|
||||||
commentId;
|
commentId;
|
||||||
|
|
||||||
constructor(public manualRedactionEntryWrapper: ManualRedactionEntryWrapper, public manualAddResponse: ManualAddResponse) {
|
constructor(
|
||||||
this.annotationId = manualAddResponse?.annotationId ? manualAddResponse.annotationId : new Date().getTime();
|
public manualRedactionEntryWrapper: ManualRedactionEntryWrapper,
|
||||||
this.commentId = manualAddResponse?.commentId ? manualAddResponse.commentId : new Date().getTime();
|
public manualAddResponse: ManualAddResponse
|
||||||
|
) {
|
||||||
|
this.annotationId = manualAddResponse?.annotationId
|
||||||
|
? manualAddResponse.annotationId
|
||||||
|
: new Date().getTime();
|
||||||
|
this.commentId = manualAddResponse?.commentId
|
||||||
|
? manualAddResponse.commentId
|
||||||
|
: new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
get dictionary() {
|
get dictionary() {
|
||||||
|
|||||||
@ -77,6 +77,13 @@ const components = [
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [...components],
|
declarations: [...components],
|
||||||
providers: [AdminDialogService],
|
providers: [AdminDialogService],
|
||||||
imports: [CommonModule, SharedModule, AdminRoutingModule, AceEditorModule, NgxChartsModule, ColorPickerModule]
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
AdminRoutingModule,
|
||||||
|
AceEditorModule,
|
||||||
|
NgxChartsModule,
|
||||||
|
ColorPickerModule
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class AdminModule {}
|
export class AdminModule {}
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
<div class="menu flex-2 visible-lg breadcrumbs-container">
|
<div class="menu flex-2 visible-lg breadcrumbs-container">
|
||||||
<a *ngIf="root || !!appStateService.activeRuleSet" [routerLink]="'/main/admin/project-templates'" class="breadcrumb" translate="project-templates"></a>
|
<a
|
||||||
|
*ngIf="root || !!appStateService.activeRuleSet"
|
||||||
|
[routerLink]="'/main/admin/project-templates'"
|
||||||
|
class="breadcrumb"
|
||||||
|
translate="project-templates"
|
||||||
|
></a>
|
||||||
|
|
||||||
<ng-container *ngIf="appStateService.activeRuleSet">
|
<ng-container *ngIf="appStateService.activeRuleSet">
|
||||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||||
@ -15,7 +20,12 @@
|
|||||||
<ng-container *ngIf="appStateService.activeDictionary">
|
<ng-container *ngIf="appStateService.activeDictionary">
|
||||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||||
<a
|
<a
|
||||||
[routerLink]="'/main/admin/project-templates/' + appStateService.activeRuleSetId + '/dictionaries/' + appStateService.activeDictionaryType"
|
[routerLink]="
|
||||||
|
'/main/admin/project-templates/' +
|
||||||
|
appStateService.activeRuleSetId +
|
||||||
|
'/dictionaries/' +
|
||||||
|
appStateService.activeDictionaryType
|
||||||
|
"
|
||||||
class="breadcrumb ml-0"
|
class="breadcrumb ml-0"
|
||||||
routerLinkActive="active"
|
routerLinkActive="active"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -1,8 +1,24 @@
|
|||||||
import { Component, ContentChild, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
ContentChild,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
ViewChild,
|
||||||
|
ViewEncapsulation
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
import { curveLinear } from 'd3-shape';
|
import { curveLinear } from 'd3-shape';
|
||||||
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
import { scaleBand, scaleLinear, scalePoint, scaleTime } from 'd3-scale';
|
||||||
import { BaseChartComponent, calculateViewDimensions, ColorHelper, LineSeriesComponent, ViewDimensions } from '@swimlane/ngx-charts';
|
import {
|
||||||
|
BaseChartComponent,
|
||||||
|
calculateViewDimensions,
|
||||||
|
ColorHelper,
|
||||||
|
LineSeriesComponent,
|
||||||
|
ViewDimensions
|
||||||
|
} from '@swimlane/ngx-charts';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
// eslint-disable-next-line @angular-eslint/component-selector
|
// eslint-disable-next-line @angular-eslint/component-selector
|
||||||
@ -261,7 +277,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
if (this.bandwidth === undefined) {
|
if (this.bandwidth === undefined) {
|
||||||
this.bandwidth = width - this.barPadding;
|
this.bandwidth = width - this.barPadding;
|
||||||
}
|
}
|
||||||
const offset = Math.floor((width + this.barPadding - (this.bandwidth + this.barPadding) * domain.length) / 2);
|
const offset = Math.floor(
|
||||||
|
(width + this.barPadding - (this.bandwidth + this.barPadding) * domain.length) / 2
|
||||||
|
);
|
||||||
|
|
||||||
if (this.scaleType === 'time') {
|
if (this.scaleType === 'time') {
|
||||||
scale = scaleTime().range([0, width]).domain(domain);
|
scale = scaleTime().range([0, width]).domain(domain);
|
||||||
@ -326,7 +344,12 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
domain = this.yDomain;
|
domain = this.yDomain;
|
||||||
}
|
}
|
||||||
this.colors = new ColorHelper(this.scheme, this.schemeType, domain, this.customColors);
|
this.colors = new ColorHelper(this.scheme, this.schemeType, domain, this.customColors);
|
||||||
this.colorsLine = new ColorHelper(this.colorSchemeLine, this.schemeType, domain, this.customColors);
|
this.colorsLine = new ColorHelper(
|
||||||
|
this.colorSchemeLine,
|
||||||
|
this.schemeType,
|
||||||
|
domain,
|
||||||
|
this.customColors
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLegendOptions() {
|
getLegendOptions() {
|
||||||
@ -364,7 +387,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onActivate(item) {
|
onActivate(item) {
|
||||||
const idx = this.activeEntries.findIndex((d) => 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) {
|
if (idx > -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -374,7 +399,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDeactivate(item) {
|
onDeactivate(item) {
|
||||||
const idx = this.activeEntries.findIndex((d) => 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.splice(idx, 1);
|
||||||
this.activeEntries = [...this.activeEntries];
|
this.activeEntries = [...this.activeEntries];
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
import { animate, style, transition, trigger } from '@angular/animations';
|
import { animate, style, transition, trigger } from '@angular/animations';
|
||||||
import { formatLabel } from '@swimlane/ngx-charts';
|
import { formatLabel } from '@swimlane/ngx-charts';
|
||||||
|
|
||||||
@ -151,7 +158,10 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
bar.gradientStops = this.colors.getLinearGradientStops(value);
|
bar.gradientStops = this.colors.getLinearGradientStops(value);
|
||||||
} else {
|
} else {
|
||||||
bar.color = this.colors.getColor(bar.offset1);
|
bar.color = this.colors.getColor(bar.offset1);
|
||||||
bar.gradientStops = this.colors.getLinearGradientStops(bar.offset1, bar.offset0);
|
bar.gradientStops = this.colors.getLinearGradientStops(
|
||||||
|
bar.offset1,
|
||||||
|
bar.offset0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +174,9 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
const lineValue = this.seriesLine[0].series[index].value;
|
const lineValue = this.seriesLine[0].series[index].value;
|
||||||
bar.tooltipText = `
|
bar.tooltipText = `
|
||||||
<span class='tooltip-label'>${tooltipLabel}</span>
|
<span class='tooltip-label'>${tooltipLabel}</span>
|
||||||
<span class='tooltip-val'> Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%</span>
|
<span class='tooltip-val'>
|
||||||
|
Y1 - ${value.toLocaleString()} • Y2 - ${lineValue.toLocaleString()}%
|
||||||
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return bar;
|
return bar;
|
||||||
@ -177,7 +189,9 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
|||||||
|
|
||||||
isActive(entry): boolean {
|
isActive(entry): boolean {
|
||||||
if (!this.activeEntries) return false;
|
if (!this.activeEntries) return false;
|
||||||
const item = this.activeEntries.find((d) => 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;
|
return item !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,15 @@ import { AppStateService } from '@state/app-state.service';
|
|||||||
export class SideNavComponent {
|
export class SideNavComponent {
|
||||||
@Input() type: 'settings' | 'project-templates';
|
@Input() type: 'settings' | 'project-templates';
|
||||||
|
|
||||||
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: [
|
settings: [
|
||||||
{ screen: 'project-templates', onlyAdmin: true },
|
{ screen: 'project-templates', onlyAdmin: true },
|
||||||
{ screen: 'digital-signature', onlyAdmin: true },
|
{ screen: 'digital-signature', onlyAdmin: true },
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
<div class="collapsed-wrapper">
|
<div class="collapsed-wrapper">
|
||||||
<redaction-circle-button (action)="toggleCollapse.emit()" icon="red:expand" tooltip="user-stats.expand" tooltipPosition="before"></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 class="all-caps-label" translate="user-stats.title"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div class="dialog-header heading-l">
|
<div class="dialog-header heading-l">
|
||||||
{{ (dictionary ? 'add-edit-dictionary.title.edit' : 'add-edit-dictionary.title.new') | translate: { name: dictionary?.type | humanize } }}
|
{{
|
||||||
|
(dictionary ? 'add-edit-dictionary.title.edit' : 'add-edit-dictionary.title.new')
|
||||||
|
| translate: { name: dictionary?.type | humanize }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form (submit)="saveDictionary()" [formGroup]="dictionaryForm">
|
<form (submit)="saveDictionary()" [formGroup]="dictionaryForm">
|
||||||
@ -15,13 +18,23 @@
|
|||||||
<div class="first-row">
|
<div class="first-row">
|
||||||
<div *ngIf="!dictionary" class="red-input-group required">
|
<div *ngIf="!dictionary" class="red-input-group required">
|
||||||
<label translate="add-edit-dictionary.form.name"></label>
|
<label translate="add-edit-dictionary.form.name"></label>
|
||||||
<input formControlName="type" name="type" placeholder="{{ 'add-edit-dictionary.form.name-placeholder' | translate }}" type="text" />
|
<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>
|
<span class="hint" translate="add-edit-dictionary.form.name-hint"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group required w-75">
|
<div class="red-input-group required w-75">
|
||||||
<label translate="add-edit-dictionary.form.rank"></label>
|
<label translate="add-edit-dictionary.form.rank"></label>
|
||||||
<input formControlName="rank" name="rank" placeholder="{{ 'add-edit-dictionary.form.rank-placeholder' | translate }}" type="number" />
|
<input
|
||||||
|
formControlName="rank"
|
||||||
|
name="rank"
|
||||||
|
placeholder="{{ 'add-edit-dictionary.form.rank-placeholder' | translate }}"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group required">
|
<div class="red-input-group required">
|
||||||
@ -41,7 +54,10 @@
|
|||||||
class="input-icon"
|
class="input-icon"
|
||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
*ngIf="!dictionaryForm.get('hexColor').value || dictionaryForm.get('hexColor').value?.length === 0"
|
*ngIf="
|
||||||
|
!dictionaryForm.get('hexColor').value ||
|
||||||
|
dictionaryForm.get('hexColor').value?.length === 0
|
||||||
|
"
|
||||||
svgIcon="red:color-picker"
|
svgIcon="red:color-picker"
|
||||||
></mat-icon>
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
@ -53,7 +69,9 @@
|
|||||||
<textarea
|
<textarea
|
||||||
formControlName="description"
|
formControlName="description"
|
||||||
name="description"
|
name="description"
|
||||||
placeholder="{{ 'add-edit-dictionary.form.description-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'add-edit-dictionary.form.description-placeholder' | translate
|
||||||
|
}}"
|
||||||
redactionHasScrollbar
|
redactionHasScrollbar
|
||||||
rows="4"
|
rows="4"
|
||||||
type="text"
|
type="text"
|
||||||
@ -62,8 +80,12 @@
|
|||||||
|
|
||||||
<div class="red-input-group slider-row">
|
<div class="red-input-group slider-row">
|
||||||
<mat-button-toggle-group appearance="legacy" formControlName="hint" name="hint">
|
<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]="false">
|
||||||
<mat-button-toggle [value]="true"> {{ 'add-edit-dictionary.form.hint' | translate }}</mat-button-toggle>
|
{{ '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>
|
</mat-button-toggle-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -74,18 +96,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="addToDictionaryAction" name="addToDictionaryAction">
|
<mat-checkbox
|
||||||
|
color="primary"
|
||||||
|
formControlName="addToDictionaryAction"
|
||||||
|
name="addToDictionaryAction"
|
||||||
|
>
|
||||||
{{ 'add-edit-dictionary.form.add-to-dictionary-action' | translate }}
|
{{ 'add-edit-dictionary.form.add-to-dictionary-action' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="dictionaryForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="dictionaryForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'add-edit-dictionary.save' | translate }}
|
{{ 'add-edit-dictionary.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -65,7 +65,11 @@ export class AddEditDictionaryDialogComponent {
|
|||||||
let observable: Observable<any>;
|
let observable: Observable<any>;
|
||||||
if (this.dictionary) {
|
if (this.dictionary) {
|
||||||
// edit mode
|
// edit mode
|
||||||
observable = this._dictionaryControllerService.updateType(typeValue, typeValue.type, this._ruleSetId);
|
observable = this._dictionaryControllerService.updateType(
|
||||||
|
typeValue,
|
||||||
|
typeValue.type,
|
||||||
|
this._ruleSetId
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// create mode
|
// create mode
|
||||||
typeValue.ruleSetId = this._ruleSetId;
|
typeValue.ruleSetId = this._ruleSetId;
|
||||||
@ -89,7 +93,11 @@ export class AddEditDictionaryDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _notifyError(message: string) {
|
private _notifyError(message: string) {
|
||||||
this._notificationService.showToastNotification(this._translateService.instant(message), null, NotificationType.ERROR);
|
this._notificationService.showToastNotification(
|
||||||
|
this._translateService.instant(message),
|
||||||
|
null,
|
||||||
|
NotificationType.ERROR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _formToObject(): TypeValue {
|
private _formToObject(): TypeValue {
|
||||||
|
|||||||
@ -1,13 +1,23 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div class="dialog-header heading-l">
|
<div class="dialog-header heading-l">
|
||||||
{{ (fileAttribute ? 'add-edit-file-attribute.title.edit' : 'add-edit-file-attribute.title.new') | translate: { name: fileAttribute?.label } }}
|
{{
|
||||||
|
(fileAttribute
|
||||||
|
? 'add-edit-file-attribute.title.edit'
|
||||||
|
: 'add-edit-file-attribute.title.new'
|
||||||
|
) | translate: { name: fileAttribute?.label }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form (submit)="saveFileAttribute()" [formGroup]="fileAttributeForm">
|
<form (submit)="saveFileAttribute()" [formGroup]="fileAttributeForm">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group required w-300">
|
||||||
<label translate="add-edit-file-attribute.form.name"></label>
|
<label translate="add-edit-file-attribute.form.name"></label>
|
||||||
<input formControlName="label" name="label" placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}" type="text" />
|
<input
|
||||||
|
formControlName="label"
|
||||||
|
name="label"
|
||||||
|
placeholder="{{ 'add-edit-file-attribute.form.name-placeholder' | translate }}"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group required w-300">
|
||||||
@ -15,7 +25,9 @@
|
|||||||
<input
|
<input
|
||||||
formControlName="csvColumnHeader"
|
formControlName="csvColumnHeader"
|
||||||
name="csvColumnHeader"
|
name="csvColumnHeader"
|
||||||
placeholder="{{ 'add-edit-file-attribute.form.column-header-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'add-edit-file-attribute.form.column-header-placeholder' | translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -31,22 +43,37 @@
|
|||||||
|
|
||||||
<div class="options-wrapper">
|
<div class="options-wrapper">
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<mat-slide-toggle color="primary" formControlName="readonly">{{ '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>
|
||||||
|
|
||||||
<div class="red-input-group mt-0">
|
<div class="red-input-group mt-0">
|
||||||
<mat-checkbox color="primary" formControlName="primaryAttribute" name="primaryAttribute">
|
<mat-checkbox
|
||||||
|
color="primary"
|
||||||
|
formControlName="primaryAttribute"
|
||||||
|
name="primaryAttribute"
|
||||||
|
>
|
||||||
{{ 'add-edit-file-attribute.form.primary' | translate }}
|
{{ 'add-edit-file-attribute.form.primary' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="fileAttributeForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="fileAttributeForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'add-edit-file-attribute.save' | translate }}
|
{{ 'add-edit-file-attribute.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -13,13 +13,18 @@ export class AddEditFileAttributeDialogComponent {
|
|||||||
fileAttributeForm: FormGroup;
|
fileAttributeForm: FormGroup;
|
||||||
fileAttribute: FileAttributeConfig;
|
fileAttribute: FileAttributeConfig;
|
||||||
ruleSetId: string;
|
ruleSetId: string;
|
||||||
readonly typeOptions = [FileAttributeConfig.TypeEnum.TEXT, FileAttributeConfig.TypeEnum.NUMBER, FileAttributeConfig.TypeEnum.DATE];
|
readonly typeOptions = [
|
||||||
|
FileAttributeConfig.TypeEnum.TEXT,
|
||||||
|
FileAttributeConfig.TypeEnum.NUMBER,
|
||||||
|
FileAttributeConfig.TypeEnum.DATE
|
||||||
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _appStateService: AppStateService,
|
private readonly _appStateService: AppStateService,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
public dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
|
public dialogRef: MatDialogRef<AddEditFileAttributeDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { fileAttribute: FileAttributeConfig; ruleSetId: string }
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: { fileAttribute: FileAttributeConfig; ruleSetId: string }
|
||||||
) {
|
) {
|
||||||
this.fileAttribute = data.fileAttribute;
|
this.fileAttribute = data.fileAttribute;
|
||||||
this.ruleSetId = data.ruleSetId;
|
this.ruleSetId = data.ruleSetId;
|
||||||
@ -27,7 +32,10 @@ export class AddEditFileAttributeDialogComponent {
|
|||||||
this.fileAttributeForm = this._formBuilder.group({
|
this.fileAttributeForm = this._formBuilder.group({
|
||||||
label: [this.fileAttribute?.label, Validators.required],
|
label: [this.fileAttribute?.label, Validators.required],
|
||||||
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
|
csvColumnHeader: [this.fileAttribute?.csvColumnHeader, Validators.required],
|
||||||
type: [this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT, Validators.required],
|
type: [
|
||||||
|
this.fileAttribute?.type || FileAttributeConfig.TypeEnum.TEXT,
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
|
readonly: [this.fileAttribute ? !this.fileAttribute.editable : false],
|
||||||
primaryAttribute: [this.fileAttribute?.primaryAttribute]
|
primaryAttribute: [this.fileAttribute?.primaryAttribute]
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,13 +1,25 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div class="dialog-header heading-l">
|
<div class="dialog-header heading-l">
|
||||||
{{ (ruleSet ? 'add-edit-project-template.title.edit' : 'add-edit-project-template.title.new') | translate: { name: ruleSet?.name } }}
|
{{
|
||||||
|
(ruleSet
|
||||||
|
? 'add-edit-project-template.title.edit'
|
||||||
|
: 'add-edit-project-template.title.new'
|
||||||
|
) | translate: { name: ruleSet?.name }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form (submit)="saveRuleSet()" [formGroup]="ruleSetForm">
|
<form (submit)="saveRuleSet()" [formGroup]="ruleSetForm">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group required w-300">
|
||||||
<label translate="add-edit-project-template.form.name"></label>
|
<label translate="add-edit-project-template.form.name"></label>
|
||||||
<input formControlName="name" name="name" placeholder="{{ 'add-edit-project-template.form.name-placeholder' | translate }}" type="text" />
|
<input
|
||||||
|
formControlName="name"
|
||||||
|
name="name"
|
||||||
|
placeholder="{{
|
||||||
|
'add-edit-project-template.form.name-placeholder' | translate
|
||||||
|
}}"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group w-400">
|
<div class="red-input-group w-400">
|
||||||
@ -15,7 +27,9 @@
|
|||||||
<textarea
|
<textarea
|
||||||
formControlName="description"
|
formControlName="description"
|
||||||
name="description"
|
name="description"
|
||||||
placeholder="{{ 'add-edit-project-template.form.description-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'add-edit-project-template.form.description-placeholder' | translate
|
||||||
|
}}"
|
||||||
rows="4"
|
rows="4"
|
||||||
type="text"
|
type="text"
|
||||||
></textarea>
|
></textarea>
|
||||||
@ -23,11 +37,21 @@
|
|||||||
|
|
||||||
<div class="validity">
|
<div class="validity">
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox (change)="hasValidFrom = !hasValidFrom" [checked]="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 }}
|
{{ 'add-edit-project-template.form.valid-from' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
|
||||||
<mat-checkbox (change)="hasValidTo = !hasValidTo" [checked]="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 }}
|
{{ 'add-edit-project-template.form.valid-to' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -35,7 +59,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="red-input-group datepicker-wrapper">
|
<div class="red-input-group datepicker-wrapper">
|
||||||
<ng-container *ngIf="hasValidFrom">
|
<ng-container *ngIf="hasValidFrom">
|
||||||
<input [matDatepicker]="fromPicker" formControlName="validFrom" placeholder="dd/mm/yy" />
|
<input
|
||||||
|
[matDatepicker]="fromPicker"
|
||||||
|
formControlName="validFrom"
|
||||||
|
placeholder="dd/mm/yy"
|
||||||
|
/>
|
||||||
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
||||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
@ -45,7 +73,11 @@
|
|||||||
|
|
||||||
<div class="red-input-group datepicker-wrapper">
|
<div class="red-input-group datepicker-wrapper">
|
||||||
<ng-container *ngIf="hasValidTo">
|
<ng-container *ngIf="hasValidTo">
|
||||||
<input [matDatepicker]="toPicker" formControlName="validTo" placeholder="dd/mm/yy" />
|
<input
|
||||||
|
[matDatepicker]="toPicker"
|
||||||
|
formControlName="validTo"
|
||||||
|
placeholder="dd/mm/yy"
|
||||||
|
/>
|
||||||
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
||||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
@ -58,14 +90,25 @@
|
|||||||
<p class="download-includes">{{ 'download-includes' | translate }}</p>
|
<p class="download-includes">{{ 'download-includes' | translate }}</p>
|
||||||
<div class="space-between">
|
<div class="space-between">
|
||||||
<redaction-select
|
<redaction-select
|
||||||
[label]="'report-type.label' | translate: { length: this.ruleSetForm.controls['reportTypes'].value.length }"
|
[label]="
|
||||||
|
'report-type.label'
|
||||||
|
| translate
|
||||||
|
: { length: this.ruleSetForm.controls['reportTypes'].value.length }
|
||||||
|
"
|
||||||
[options]="reportTypesEnum"
|
[options]="reportTypesEnum"
|
||||||
[translatePrefix]="'report-type.'"
|
[translatePrefix]="'report-type.'"
|
||||||
class="w-410"
|
class="w-410"
|
||||||
formControlName="reportTypes"
|
formControlName="reportTypes"
|
||||||
></redaction-select>
|
></redaction-select>
|
||||||
<redaction-select
|
<redaction-select
|
||||||
[label]="'download-type.label' | translate: { length: this.ruleSetForm.controls['downloadFileTypes'].value.length }"
|
[label]="
|
||||||
|
'download-type.label'
|
||||||
|
| translate
|
||||||
|
: {
|
||||||
|
length: this.ruleSetForm.controls['downloadFileTypes'].value
|
||||||
|
.length
|
||||||
|
}
|
||||||
|
"
|
||||||
[options]="downloadTypesEnum"
|
[options]="downloadTypesEnum"
|
||||||
[translatePrefix]="'download-type.'"
|
[translatePrefix]="'download-type.'"
|
||||||
class="w-410"
|
class="w-410"
|
||||||
@ -75,11 +118,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="ruleSetForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="ruleSetForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'add-edit-project-template.save' | translate }}
|
{{ 'add-edit-project-template.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -32,13 +32,25 @@ export class AddEditRuleSetDialogComponent {
|
|||||||
this.ruleSetForm = this._formBuilder.group({
|
this.ruleSetForm = this._formBuilder.group({
|
||||||
name: [this.ruleSet?.name, Validators.required],
|
name: [this.ruleSet?.name, Validators.required],
|
||||||
description: [this.ruleSet?.description],
|
description: [this.ruleSet?.description],
|
||||||
validFrom: [this.ruleSet?.validFrom ? moment(this.ruleSet?.validFrom) : null, this._requiredIfValidator(() => this.hasValidFrom)],
|
validFrom: [
|
||||||
validTo: [this.ruleSet?.validTo ? moment(this.ruleSet?.validTo) : null, this._requiredIfValidator(() => this.hasValidTo)],
|
this.ruleSet?.validFrom ? moment(this.ruleSet?.validFrom) : null,
|
||||||
downloadFileTypes: [this.ruleSet ? this.ruleSet.downloadFileTypes : ['PREVIEW', 'REDACTED']],
|
this._requiredIfValidator(() => this.hasValidFrom)
|
||||||
|
],
|
||||||
|
validTo: [
|
||||||
|
this.ruleSet?.validTo ? moment(this.ruleSet?.validTo) : null,
|
||||||
|
this._requiredIfValidator(() => this.hasValidTo)
|
||||||
|
],
|
||||||
|
downloadFileTypes: [
|
||||||
|
this.ruleSet ? this.ruleSet.downloadFileTypes : ['PREVIEW', 'REDACTED']
|
||||||
|
],
|
||||||
reportTypes: [
|
reportTypes: [
|
||||||
this.ruleSet
|
this.ruleSet
|
||||||
? this.ruleSet.reportTypes
|
? this.ruleSet.reportTypes
|
||||||
: ['WORD_SINGLE_FILE_APPENDIX_A1_TEMPLATE', 'WORD_SINGLE_FILE_APPENDIX_A2_TEMPLATE', 'EXCEL_MULTI_FILE'],
|
: [
|
||||||
|
'WORD_SINGLE_FILE_APPENDIX_A1_TEMPLATE',
|
||||||
|
'WORD_SINGLE_FILE_APPENDIX_A2_TEMPLATE',
|
||||||
|
'EXCEL_MULTI_FILE'
|
||||||
|
],
|
||||||
Validators.required
|
Validators.required
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@ -61,14 +73,24 @@ export class AddEditRuleSetDialogComponent {
|
|||||||
if (this.hasValidFrom !== !!this.ruleSet.validFrom) {
|
if (this.hasValidFrom !== !!this.ruleSet.validFrom) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.hasValidFrom && !moment(this.ruleSet.validFrom).isSame(moment(this.ruleSetForm.get('validFrom').value))) {
|
if (
|
||||||
|
this.hasValidFrom &&
|
||||||
|
!moment(this.ruleSet.validFrom).isSame(
|
||||||
|
moment(this.ruleSetForm.get('validFrom').value)
|
||||||
|
)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (key === 'validTo') {
|
} else if (key === 'validTo') {
|
||||||
if (this.hasValidTo !== !!this.ruleSet.validTo) {
|
if (this.hasValidTo !== !!this.ruleSet.validTo) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.hasValidTo && !moment(this.ruleSet.validTo).isSame(moment(this.ruleSetForm.get('validTo').value))) {
|
if (
|
||||||
|
this.hasValidTo &&
|
||||||
|
!moment(this.ruleSet.validTo).isSame(
|
||||||
|
moment(this.ruleSetForm.get('validTo').value)
|
||||||
|
)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (this.ruleSet[key] !== this.ruleSetForm.get(key).value) {
|
} else if (this.ruleSet[key] !== this.ruleSetForm.get(key).value) {
|
||||||
@ -93,7 +115,16 @@ export class AddEditRuleSetDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _applyValidityIntervalConstraints(value): boolean {
|
private _applyValidityIntervalConstraints(value): boolean {
|
||||||
if (applyIntervalConstraints(value, this._previousValidFrom, this._previousValidTo, this.ruleSetForm, 'validFrom', 'validTo')) {
|
if (
|
||||||
|
applyIntervalConstraints(
|
||||||
|
value,
|
||||||
|
this._previousValidFrom,
|
||||||
|
this._previousValidTo,
|
||||||
|
this.ruleSetForm,
|
||||||
|
'validFrom',
|
||||||
|
'validTo'
|
||||||
|
)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,7 +28,11 @@
|
|||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="add-edit-user.form.role"></label>
|
<label translate="add-edit-user.form.role"></label>
|
||||||
<div class="roles-wrapper">
|
<div class="roles-wrapper">
|
||||||
<mat-checkbox *ngFor="let role of ROLES" [formControlName]="role" color="primary">
|
<mat-checkbox
|
||||||
|
*ngFor="let role of ROLES"
|
||||||
|
[formControlName]="role"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
{{ 'roles.' + role | translate }}
|
{{ 'roles.' + role | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -36,15 +40,37 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="userForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
{{ (user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate }}
|
[disabled]="userForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
(user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save')
|
||||||
|
| translate
|
||||||
|
}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<redaction-icon-button (action)="delete()" *ngIf="user" icon="red:trash" text="add-edit-user.actions.delete" type="show-bg"></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
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
mat-dialog-close
|
||||||
|
translate="add-edit-user.actions.cancel"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -29,7 +29,10 @@ export class AddEditUserDialogComponent {
|
|||||||
disabled:
|
disabled:
|
||||||
this.user &&
|
this.user &&
|
||||||
Object.keys(this._ROLE_REQUIREMENTS).reduce(
|
Object.keys(this._ROLE_REQUIREMENTS).reduce(
|
||||||
(value, key) => value || (role === this._ROLE_REQUIREMENTS[key] && this.user.roles.indexOf(key) !== -1),
|
(value, key) =>
|
||||||
|
value ||
|
||||||
|
(role === this._ROLE_REQUIREMENTS[key] &&
|
||||||
|
this.user.roles.indexOf(key) !== -1),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -40,7 +43,10 @@ export class AddEditUserDialogComponent {
|
|||||||
this.userForm = this._formBuilder.group({
|
this.userForm = this._formBuilder.group({
|
||||||
firstName: [this.user?.firstName, Validators.required],
|
firstName: [this.user?.firstName, Validators.required],
|
||||||
lastName: [this.user?.lastName, Validators.required],
|
lastName: [this.user?.lastName, Validators.required],
|
||||||
email: [{ value: this.user?.email, disabled: !!user }, [Validators.required, Validators.email]],
|
email: [
|
||||||
|
{ value: this.user?.email, disabled: !!user },
|
||||||
|
[Validators.required, Validators.email]
|
||||||
|
],
|
||||||
// password: [this.user?.password, Validators.required],
|
// password: [this.user?.password, Validators.required],
|
||||||
...rolesControls
|
...rolesControls
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div class="dialog-header heading-l">
|
<div class="dialog-header heading-l">
|
||||||
{{ 'confirm-delete-file-attribute.title.' + type | translate: { name: fileAttribute?.label } }}
|
{{
|
||||||
|
'confirm-delete-file-attribute.title.' + type
|
||||||
|
| translate: { name: fileAttribute?.label }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
<div *ngIf="showToast" class="inline-dialog-toast toast-error">
|
||||||
@ -27,7 +30,15 @@
|
|||||||
<button (click)="deleteFileAttribute()" color="primary" mat-flat-button>
|
<button (click)="deleteFileAttribute()" color="primary" mat-flat-button>
|
||||||
{{ 'confirm-delete-file-attribute.delete.' + type | translate }}
|
{{ 'confirm-delete-file-attribute.delete.' + type | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div (click)="cancel()" [translate]="'confirm-delete-file-attribute.cancel.' + type" class="all-caps-label cancel"></div>
|
<div
|
||||||
|
(click)="cancel()"
|
||||||
|
[translate]="'confirm-delete-file-attribute.cancel.' + type"
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -17,7 +17,10 @@
|
|||||||
[class.error]="!checkbox.value && showToast"
|
[class.error]="!checkbox.value && showToast"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{{ 'confirm-delete-users.' + checkbox.label | translate: { projectsCount: projectsCount } }}
|
{{
|
||||||
|
'confirm-delete-users.' + checkbox.label
|
||||||
|
| translate: { projectsCount: projectsCount }
|
||||||
|
}}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -25,7 +28,15 @@
|
|||||||
<button (click)="deleteUser()" color="primary" mat-flat-button>
|
<button (click)="deleteUser()" color="primary" mat-flat-button>
|
||||||
{{ 'confirm-delete-users.delete.' + type | translate }}
|
{{ 'confirm-delete-users.delete.' + type | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div (click)="cancel()" [translate]="'confirm-delete-users.cancel.' + type" class="all-caps-label cancel"></div>
|
<div
|
||||||
|
(click)="cancel()"
|
||||||
|
[translate]="'confirm-delete-users.cancel.' + type"
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<div [translate]="'default-colors-screen.types.' + this.colorKey" class="dialog-header heading-l"></div>
|
<div
|
||||||
|
[translate]="'default-colors-screen.types.' + this.colorKey"
|
||||||
|
class="dialog-header heading-l"
|
||||||
|
></div>
|
||||||
|
|
||||||
<form (submit)="saveColors()" [formGroup]="colorForm">
|
<form (submit)="saveColors()" [formGroup]="colorForm">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
@ -19,17 +22,32 @@
|
|||||||
[style.background]="colorForm.get('color').value"
|
[style.background]="colorForm.get('color').value"
|
||||||
class="input-icon"
|
class="input-icon"
|
||||||
>
|
>
|
||||||
<mat-icon *ngIf="!colorForm.get('color').value || colorForm.get('color').value?.length === 0" svgIcon="red:color-picker"></mat-icon>
|
<mat-icon
|
||||||
|
*ngIf="
|
||||||
|
!colorForm.get('color').value ||
|
||||||
|
colorForm.get('color').value?.length === 0
|
||||||
|
"
|
||||||
|
svgIcon="red:color-picker"
|
||||||
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="colorForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="colorForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'edit-color-dialog.save' | translate }}
|
{{ 'edit-color-dialog.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -23,7 +23,8 @@ export class EditColorDialogComponent {
|
|||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
public dialogRef: MatDialogRef<EditColorDialogComponent>,
|
public dialogRef: MatDialogRef<EditColorDialogComponent>,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { colors: Colors; colorKey: string; ruleSetId: string }
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: { colors: Colors; colorKey: string; ruleSetId: string }
|
||||||
) {
|
) {
|
||||||
this.colors = data.colors;
|
this.colors = data.colors;
|
||||||
this.colorKey = data.colorKey;
|
this.colorKey = data.colorKey;
|
||||||
@ -50,11 +51,17 @@ export class EditColorDialogComponent {
|
|||||||
this.dialogRef.close(true);
|
this.dialogRef.close(true);
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('edit-color-dialog.success', {
|
this._translateService.instant('edit-color-dialog.success', {
|
||||||
color: this._translateService.instant('default-colors-screen.types.' + this.colorKey)
|
color: this._translateService.instant(
|
||||||
|
'default-colors-screen.types.' + this.colorKey
|
||||||
|
)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._notificationService.showToastNotification(this._translateService.instant('edit-color-dialog.error'), null, NotificationType.ERROR);
|
this._notificationService.showToastNotification(
|
||||||
|
this._translateService.instant('edit-color-dialog.error'),
|
||||||
|
null,
|
||||||
|
NotificationType.ERROR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,10 @@
|
|||||||
></redaction-round-checkbox>
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'file-attributes-csv-import.table-header.title' | translate: { length: allEntities.length } }}
|
{{
|
||||||
|
'file-attributes-csv-import.table-header.title'
|
||||||
|
| translate: { length: allEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ng-container *ngIf="areSomeEntitiesSelected">
|
<ng-container *ngIf="areSomeEntitiesSelected">
|
||||||
@ -29,7 +32,10 @@
|
|||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
|
||||||
<redaction-chevron-button [matMenuTriggerFor]="typeMenu" text="file-attributes-csv-import.table-header.actions.type"></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">
|
<mat-menu #readOnlyMenu="matMenu" class="no-padding-bottom">
|
||||||
<button
|
<button
|
||||||
@ -45,7 +51,11 @@
|
|||||||
</mat-menu>
|
</mat-menu>
|
||||||
|
|
||||||
<mat-menu #typeMenu="matMenu" class="no-padding-bottom">
|
<mat-menu #typeMenu="matMenu" class="no-padding-bottom">
|
||||||
<button (click)="setAttributeForSelection('type', type)" *ngFor="let type of typeOptions" mat-menu-item>
|
<button
|
||||||
|
(click)="setAttributeForSelection('type', type)"
|
||||||
|
*ngFor="let type of typeOptions"
|
||||||
|
mat-menu-item
|
||||||
|
>
|
||||||
{{ 'file-attribute-types.' + type | translate }}
|
{{ 'file-attribute-types.' + type | translate }}
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
@ -55,9 +65,14 @@
|
|||||||
<div class="table-header" redactionSyncWidth="table-item">
|
<div class="table-header" redactionSyncWidth="table-item">
|
||||||
<div class="select-oval-placeholder"></div>
|
<div class="select-oval-placeholder"></div>
|
||||||
|
|
||||||
<redaction-table-col-name class="name" label="file-attributes-csv-import.table-col-names.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.type"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
class="flex-center"
|
class="flex-center"
|
||||||
@ -76,7 +91,11 @@
|
|||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</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>
|
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
|
||||||
<!-- Table lines -->
|
<!-- Table lines -->
|
||||||
@ -93,7 +112,10 @@
|
|||||||
<div *ngIf="!field.editingName">
|
<div *ngIf="!field.editingName">
|
||||||
{{ field.name }}
|
{{ field.name }}
|
||||||
</div>
|
</div>
|
||||||
<form (submit)="field.editingName = false; field.name = field.temporaryName" *ngIf="field.editingName">
|
<form
|
||||||
|
(submit)="field.editingName = false; field.name = field.temporaryName"
|
||||||
|
*ngIf="field.editingName"
|
||||||
|
>
|
||||||
<div class="red-input-group w-200">
|
<div class="red-input-group w-200">
|
||||||
<input [(ngModel)]="field.temporaryName" name="name" />
|
<input [(ngModel)]="field.temporaryName" name="name" />
|
||||||
</div>
|
</div>
|
||||||
@ -139,7 +161,10 @@
|
|||||||
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
|
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<redaction-round-checkbox (click)="togglePrimary(field)" [active]="field.primaryAttribute"></redaction-round-checkbox>
|
<redaction-round-checkbox
|
||||||
|
(click)="togglePrimary(field)"
|
||||||
|
[active]="field.primaryAttribute"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
|
|||||||
@ -1,4 +1,12 @@
|
|||||||
import { Component, EventEmitter, Injector, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
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 { Field } from '../file-attributes-csv-import-dialog.component';
|
||||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||||
@ -14,7 +22,11 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
|
|||||||
@Output() setHoveredColumn = new EventEmitter<string>();
|
@Output() setHoveredColumn = new EventEmitter<string>();
|
||||||
@Output() toggleFieldActive = new EventEmitter<Field>();
|
@Output() toggleFieldActive = new EventEmitter<Field>();
|
||||||
|
|
||||||
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';
|
protected readonly _selectionKey = 'csvColumn';
|
||||||
|
|
||||||
@ -30,7 +42,9 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
|
|||||||
}
|
}
|
||||||
|
|
||||||
deactivateSelection() {
|
deactivateSelection() {
|
||||||
this.allEntities.filter((field) => this.isEntitySelected(field)).forEach((field) => (field.primaryAttribute = false));
|
this.allEntities
|
||||||
|
.filter((field) => this.isEntitySelected(field))
|
||||||
|
.forEach((field) => (field.primaryAttribute = false));
|
||||||
this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))];
|
this.allEntities = [...this.allEntities.filter((field) => !this.isEntitySelected(field))];
|
||||||
this.allEntitiesChange.emit(this.allEntities);
|
this.allEntitiesChange.emit(this.allEntities);
|
||||||
this.selectedEntitiesIds = [];
|
this.selectedEntitiesIds = [];
|
||||||
|
|||||||
@ -4,25 +4,37 @@
|
|||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="sub-header">
|
<div class="sub-header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="info"><span translate="file-attributes-csv-import.file"> </span> {{ csvFile.name }}</div>
|
<div class="info">
|
||||||
|
<span translate="file-attributes-csv-import.file"> </span> {{ csvFile.name }}
|
||||||
|
</div>
|
||||||
<div class="large-label">
|
<div class="large-label">
|
||||||
{{ 'file-attributes-csv-import.total-rows' | translate: { rows: parseResult?.data?.length } }}
|
{{
|
||||||
|
'file-attributes-csv-import.total-rows'
|
||||||
|
| translate: { rows: parseResult?.data?.length }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
|
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
|
||||||
<div class="red-input-group required w-250">
|
<div class="red-input-group required w-250">
|
||||||
<mat-form-field floatLabel="always">
|
<mat-form-field floatLabel="always">
|
||||||
<mat-label>{{ 'file-attributes-csv-import.key-column' | translate }}</mat-label>
|
<mat-label>{{
|
||||||
|
'file-attributes-csv-import.key-column' | translate
|
||||||
|
}}</mat-label>
|
||||||
<input
|
<input
|
||||||
[matAutocomplete]="auto"
|
[matAutocomplete]="auto"
|
||||||
[placeholder]="'file-attributes-csv-import.key-column-placeholder' | translate"
|
[placeholder]="
|
||||||
|
'file-attributes-csv-import.key-column-placeholder' | translate
|
||||||
|
"
|
||||||
formControlName="filenameMappingColumnHeaderName"
|
formControlName="filenameMappingColumnHeaderName"
|
||||||
matInput
|
matInput
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
|
<mat-autocomplete #auto="matAutocomplete" autoActiveFirstOption>
|
||||||
<mat-option *ngFor="let field of filteredKeyOptions | async" [value]="field">
|
<mat-option
|
||||||
|
*ngFor="let field of filteredKeyOptions | async"
|
||||||
|
[value]="field"
|
||||||
|
>
|
||||||
{{ field }}
|
{{ field }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-autocomplete>
|
</mat-autocomplete>
|
||||||
@ -32,7 +44,9 @@
|
|||||||
<div class="red-input-group required w-110">
|
<div class="red-input-group required w-110">
|
||||||
<label translate="file-attributes-csv-import.delimiter"></label>
|
<label translate="file-attributes-csv-import.delimiter"></label>
|
||||||
<input
|
<input
|
||||||
[placeholder]="'file-attributes-csv-import.delimiter-placeholder' | translate"
|
[placeholder]="
|
||||||
|
'file-attributes-csv-import.delimiter-placeholder' | translate
|
||||||
|
"
|
||||||
formControlName="delimiter"
|
formControlName="delimiter"
|
||||||
name="delimiter"
|
name="delimiter"
|
||||||
type="text"
|
type="text"
|
||||||
@ -42,7 +56,9 @@
|
|||||||
<div class="red-input-group required w-160">
|
<div class="red-input-group required w-160">
|
||||||
<label translate="file-attributes-csv-import.encoding"></label>
|
<label translate="file-attributes-csv-import.encoding"></label>
|
||||||
<input
|
<input
|
||||||
[placeholder]="'file-attributes-csv-import.encoding-placeholder' | translate"
|
[placeholder]="
|
||||||
|
'file-attributes-csv-import.encoding-placeholder' | translate
|
||||||
|
"
|
||||||
formControlName="encoding"
|
formControlName="encoding"
|
||||||
name="encoding"
|
name="encoding"
|
||||||
type="text"
|
type="text"
|
||||||
@ -62,9 +78,13 @@
|
|||||||
<div class="csv-part-header">
|
<div class="csv-part-header">
|
||||||
<div>
|
<div>
|
||||||
<span class="all-caps-label">{{
|
<span class="all-caps-label">{{
|
||||||
'file-attributes-csv-import.available' | translate: { value: parseResult?.meta?.fields.length }
|
'file-attributes-csv-import.available'
|
||||||
|
| translate: { value: parseResult?.meta?.fields.length }
|
||||||
|
}}</span>
|
||||||
|
<span class="all-caps-label">{{
|
||||||
|
'file-attributes-csv-import.selected'
|
||||||
|
| translate: { value: activeFields.length }
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="all-caps-label">{{ 'file-attributes-csv-import.selected' | translate: { value: activeFields.length } }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
@ -106,17 +126,29 @@
|
|||||||
{{ field.csvColumn }}
|
{{ field.csvColumn }}
|
||||||
</div>
|
</div>
|
||||||
<div class="secondary">
|
<div class="secondary">
|
||||||
<div class="entry-count small-label">{{ getEntries(field.csvColumn) }} entries</div>
|
<div class="entry-count small-label">
|
||||||
<div class="sample small-label">Sample: {{ getSample(field.csvColumn) }}</div>
|
{{ getEntries(field.csvColumn) }} entries
|
||||||
|
</div>
|
||||||
|
<div class="sample small-label">
|
||||||
|
Sample: {{ getSample(field.csvColumn) }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div (mouseenter)="keepPreview = true" (mouseleave)="keepPreview = false; setHoveredColumn()" [class.collapsed]="!previewExpanded" class="center">
|
<div
|
||||||
|
(mouseenter)="keepPreview = true"
|
||||||
|
(mouseleave)="keepPreview = false; setHoveredColumn()"
|
||||||
|
[class.collapsed]="!previewExpanded"
|
||||||
|
class="center"
|
||||||
|
>
|
||||||
<div class="csv-part-header">
|
<div class="csv-part-header">
|
||||||
<div class="all-caps-label">
|
<div class="all-caps-label">
|
||||||
{{ 'file-attributes-csv-import.csv-column' + (previewExpanded ? '' : '-preview') | translate }}
|
{{
|
||||||
|
'file-attributes-csv-import.csv-column' +
|
||||||
|
(previewExpanded ? '' : '-preview') | translate
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(click)="previewExpanded = !previewExpanded"
|
(click)="previewExpanded = !previewExpanded"
|
||||||
@ -124,9 +156,16 @@
|
|||||||
></redaction-circle-button>
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
<div [class.hidden]="!previewExpanded" class="csv-part-content">
|
<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"
|
||||||
|
class="no-column-data"
|
||||||
|
translate="file-attributes-csv-import.no-hovered-column"
|
||||||
|
></div>
|
||||||
<div *ngIf="hoveredColumn && !columnSample.length" class="no-column-data">
|
<div *ngIf="hoveredColumn && !columnSample.length" class="no-column-data">
|
||||||
{{ 'file-attributes-csv-import.no-sample-data-for' | translate: { column: hoveredColumn } }}
|
{{
|
||||||
|
'file-attributes-csv-import.no-sample-data-for'
|
||||||
|
| translate: { column: hoveredColumn }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div *ngFor="let row of columnSample">
|
<div *ngFor="let row of columnSample">
|
||||||
{{ row }}
|
{{ row }}
|
||||||
@ -144,12 +183,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button (click)="save()" [disabled]="changedParseConfig || baseConfigForm.invalid" color="primary" mat-flat-button>
|
<button
|
||||||
|
(click)="save()"
|
||||||
|
[disabled]="changedParseConfig || baseConfigForm.invalid"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
>
|
||||||
{{ 'file-attributes-csv-import.save.label' | translate }}
|
{{ 'file-attributes-csv-import.save.label' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div (click)="dialogRef.close()" class="all-caps-label cancel">{{ 'file-attributes-csv-import.cancel' | translate }}</div>
|
<div (click)="dialogRef.close()" class="all-caps-label cancel">
|
||||||
|
{{ 'file-attributes-csv-import.cancel' | translate }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -3,7 +3,11 @@ import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/fo
|
|||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import * as Papa from 'papaparse';
|
import * as Papa from 'papaparse';
|
||||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
import {
|
||||||
|
FileAttributeConfig,
|
||||||
|
FileAttributesConfig,
|
||||||
|
FileAttributesControllerService
|
||||||
|
} from '@redaction/red-ui-http';
|
||||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
@ -40,7 +44,8 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
keepPreview = false;
|
keepPreview = false;
|
||||||
columnSample = [];
|
columnSample = [];
|
||||||
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||||
@ViewChild(CdkVirtualScrollViewport, { static: false }) cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
@ViewChild(CdkVirtualScrollViewport, { static: false })
|
||||||
|
cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||||
protected readonly _searchKey = 'csvColumn';
|
protected readonly _searchKey = 'csvColumn';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -50,14 +55,18 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
private readonly _notificationService: NotificationService,
|
private readonly _notificationService: NotificationService,
|
||||||
public dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
|
public dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
|
||||||
protected readonly _injector: Injector,
|
protected readonly _injector: Injector,
|
||||||
@Inject(MAT_DIALOG_DATA) public data: { csv: File; ruleSetId: string; existingConfiguration: FileAttributesConfig }
|
@Inject(MAT_DIALOG_DATA)
|
||||||
|
public data: { csv: File; ruleSetId: string; existingConfiguration: FileAttributesConfig }
|
||||||
) {
|
) {
|
||||||
super(_injector);
|
super(_injector);
|
||||||
this.csvFile = data.csv;
|
this.csvFile = data.csv;
|
||||||
this.ruleSetId = data.ruleSetId;
|
this.ruleSetId = data.ruleSetId;
|
||||||
|
|
||||||
this.baseConfigForm = this._formBuilder.group({
|
this.baseConfigForm = this._formBuilder.group({
|
||||||
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
|
filenameMappingColumnHeaderName: [
|
||||||
|
'',
|
||||||
|
[Validators.required, this._autocompleteStringValidator()]
|
||||||
|
],
|
||||||
delimiter: [undefined, Validators.required],
|
delimiter: [undefined, Validators.required],
|
||||||
encoding: ['UTF-8', Validators.required]
|
encoding: ['UTF-8', Validators.required]
|
||||||
});
|
});
|
||||||
@ -87,12 +96,16 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
this.parseResult.meta.fields = Object.keys(this.parseResult.data[0]);
|
this.parseResult.meta.fields = Object.keys(this.parseResult.data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.allEntities = this.parseResult.meta.fields.map((field) => this._buildAttribute(field));
|
this.allEntities = this.parseResult.meta.fields.map((field) =>
|
||||||
|
this._buildAttribute(field)
|
||||||
|
);
|
||||||
this.displayedEntities = [...this.allEntities];
|
this.displayedEntities = [...this.allEntities];
|
||||||
this.activeFields = [];
|
this.activeFields = [];
|
||||||
|
|
||||||
for (const entity of this.allEntities) {
|
for (const entity of this.allEntities) {
|
||||||
const existing = this.data.existingConfiguration.fileAttributeConfigs.find((a) => a.csvColumnHeader === entity.csvColumn);
|
const existing = this.data.existingConfiguration.fileAttributeConfigs.find(
|
||||||
|
(a) => a.csvColumnHeader === entity.csvColumn
|
||||||
|
);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
entity.id = existing.id;
|
entity.id = existing.id;
|
||||||
entity.name = existing.label;
|
entity.name = existing.label;
|
||||||
@ -104,18 +117,35 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.filteredKeyOptions = this.baseConfigForm.get('filenameMappingColumnHeaderName').valueChanges.pipe(
|
this.filteredKeyOptions = this.baseConfigForm
|
||||||
startWith(this.baseConfigForm.get('filenameMappingColumnHeaderName').value as string),
|
.get('filenameMappingColumnHeaderName')
|
||||||
map((value: string) =>
|
.valueChanges.pipe(
|
||||||
this.allEntities.filter((field) => field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !== -1).map((field) => field.csvColumn)
|
startWith(
|
||||||
)
|
this.baseConfigForm.get('filenameMappingColumnHeaderName').value as string
|
||||||
);
|
),
|
||||||
|
map((value: string) =>
|
||||||
|
this.allEntities
|
||||||
|
.filter(
|
||||||
|
(field) =>
|
||||||
|
field.csvColumn.toLowerCase().indexOf(value.toLowerCase()) !==
|
||||||
|
-1
|
||||||
|
)
|
||||||
|
.map((field) => field.csvColumn)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.data.existingConfiguration &&
|
this.data.existingConfiguration &&
|
||||||
this.allEntities.find((entity) => entity.csvColumn === this.data.existingConfiguration.filenameMappingColumnHeaderName)
|
this.allEntities.find(
|
||||||
|
(entity) =>
|
||||||
|
entity.csvColumn ===
|
||||||
|
this.data.existingConfiguration.filenameMappingColumnHeaderName
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
this.baseConfigForm.patchValue({ filenameMappingColumnHeaderName: this.data.existingConfiguration.filenameMappingColumnHeaderName });
|
this.baseConfigForm.patchValue({
|
||||||
|
filenameMappingColumnHeaderName:
|
||||||
|
this.data.existingConfiguration.filenameMappingColumnHeaderName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialParseConfig = {
|
this.initialParseConfig = {
|
||||||
@ -169,14 +199,17 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
const newPrimary = !!this.activeFields.find((attr) => attr.primaryAttribute);
|
const newPrimary = !!this.activeFields.find((attr) => attr.primaryAttribute);
|
||||||
|
|
||||||
if (newPrimary) {
|
if (newPrimary) {
|
||||||
this.data.existingConfiguration.fileAttributeConfigs.forEach((attr) => (attr.primaryAttribute = false));
|
this.data.existingConfiguration.fileAttributeConfigs.forEach(
|
||||||
|
(attr) => (attr.primaryAttribute = false)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileAttributes = {
|
const fileAttributes = {
|
||||||
...this.baseConfigForm.getRawValue(),
|
...this.baseConfigForm.getRawValue(),
|
||||||
fileAttributeConfigs: [
|
fileAttributeConfigs: [
|
||||||
...this.data.existingConfiguration.fileAttributeConfigs.filter(
|
...this.data.existingConfiguration.fileAttributeConfigs.filter(
|
||||||
(a) => !this.allEntities.find((entity) => entity.csvColumn === a.csvColumnHeader)
|
(a) =>
|
||||||
|
!this.allEntities.find((entity) => entity.csvColumn === a.csvColumnHeader)
|
||||||
),
|
),
|
||||||
...this.activeFields.map((field) => ({
|
...this.activeFields.map((field) => ({
|
||||||
id: field.id,
|
id: field.id,
|
||||||
@ -190,9 +223,13 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.ruleSetId).toPromise();
|
await this._fileAttributesControllerService
|
||||||
|
.setFileAttributesConfig(fileAttributes, this.ruleSetId)
|
||||||
|
.toPromise();
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('file-attributes-csv-import.save.success', { count: this.activeFields.length }),
|
this._translateService.instant('file-attributes-csv-import.save.success', {
|
||||||
|
count: this.activeFields.length
|
||||||
|
}),
|
||||||
null,
|
null,
|
||||||
NotificationType.SUCCESS
|
NotificationType.SUCCESS
|
||||||
);
|
);
|
||||||
@ -217,7 +254,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
if (!column) {
|
if (!column) {
|
||||||
this.columnSample = [];
|
this.columnSample = [];
|
||||||
} else {
|
} else {
|
||||||
this.columnSample = this.parseResult.data.filter((row) => !!row[column]).map((row) => row[column]);
|
this.columnSample = this.parseResult.data
|
||||||
|
.filter((row) => !!row[column])
|
||||||
|
.map((row) => row[column]);
|
||||||
}
|
}
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
@ -238,7 +277,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
|||||||
csvColumn,
|
csvColumn,
|
||||||
name: csvColumn,
|
name: csvColumn,
|
||||||
temporaryName: csvColumn,
|
temporaryName: csvColumn,
|
||||||
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
|
type: isNumber
|
||||||
|
? FileAttributeConfig.TypeEnum.NUMBER
|
||||||
|
: FileAttributeConfig.TypeEnum.TEXT,
|
||||||
readonly: false,
|
readonly: false,
|
||||||
primaryAttribute: false
|
primaryAttribute: false
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,12 @@
|
|||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group required w-300">
|
||||||
<label translate="smtp-auth-config.form.username"></label>
|
<label translate="smtp-auth-config.form.username"></label>
|
||||||
<input formControlName="user" name="user" placeholder="{{ 'smtp-auth-config.form.username-placeholder' | translate }}" type="text" />
|
<input
|
||||||
|
formControlName="user"
|
||||||
|
name="user"
|
||||||
|
placeholder="{{ 'smtp-auth-config.form.username-placeholder' | translate }}"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group required w-300">
|
<div class="red-input-group required w-300">
|
||||||
@ -18,9 +23,17 @@
|
|||||||
<button [disabled]="authForm.invalid" color="primary" mat-flat-button type="submit">
|
<button [disabled]="authForm.invalid" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'smtp-auth-config.actions.save' | translate }}
|
{{ 'smtp-auth-config.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
<div class="all-caps-label cancel" mat-dialog-close translate="smtp-auth-config.actions.cancel"></div>
|
<div
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
mat-dialog-close
|
||||||
|
translate="smtp-auth-config.actions.cancel"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<redaction-circle-button class="dialog-close" icon="red:close" mat-dialog-close></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
class="dialog-close"
|
||||||
|
icon="red:close"
|
||||||
|
mat-dialog-close
|
||||||
|
></redaction-circle-button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@ -22,7 +22,10 @@
|
|||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="header-item">
|
<div class="header-item">
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'audit-screen.table-header.title' | translate: { length: logs?.totalHits || 0 } }}
|
{{
|
||||||
|
'audit-screen.table-header.title'
|
||||||
|
| translate: { length: logs?.totalHits || 0 }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<div class="actions-wrapper">
|
<div class="actions-wrapper">
|
||||||
<redaction-pagination
|
<redaction-pagination
|
||||||
@ -34,7 +37,10 @@
|
|||||||
<div class="red-input-group w-150 mr-20">
|
<div class="red-input-group w-150 mr-20">
|
||||||
<mat-form-field class="no-label">
|
<mat-form-field class="no-label">
|
||||||
<mat-select formControlName="category">
|
<mat-select formControlName="category">
|
||||||
<mat-option *ngFor="let category of categories" [value]="category">
|
<mat-option
|
||||||
|
*ngFor="let category of categories"
|
||||||
|
[value]="category"
|
||||||
|
>
|
||||||
{{ category | translate }}
|
{{ category | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
@ -50,7 +56,10 @@
|
|||||||
[withName]="true"
|
[withName]="true"
|
||||||
size="small"
|
size="small"
|
||||||
></redaction-initials-avatar>
|
></redaction-initials-avatar>
|
||||||
<div *ngIf="filterForm.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
<div
|
||||||
|
*ngIf="filterForm.get('userId').value === ALL_USERS"
|
||||||
|
[translate]="ALL_USERS"
|
||||||
|
></div>
|
||||||
</mat-select-trigger>
|
</mat-select-trigger>
|
||||||
<mat-option *ngFor="let userId of userIds" [value]="userId">
|
<mat-option *ngFor="let userId of userIds" [value]="userId">
|
||||||
<redaction-initials-avatar
|
<redaction-initials-avatar
|
||||||
@ -59,16 +68,26 @@
|
|||||||
[withName]="true"
|
[withName]="true"
|
||||||
size="small"
|
size="small"
|
||||||
></redaction-initials-avatar>
|
></redaction-initials-avatar>
|
||||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
<div
|
||||||
|
*ngIf="userId === ALL_USERS"
|
||||||
|
[translate]="ALL_USERS"
|
||||||
|
></div>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator">·</div>
|
<div class="separator">·</div>
|
||||||
<div class="red-input-group datepicker-wrapper mr-20">
|
<div class="red-input-group datepicker-wrapper mr-20">
|
||||||
<input [matDatepicker]="fromPicker" formControlName="from" placeholder="dd/mm/yy" />
|
<input
|
||||||
|
[matDatepicker]="fromPicker"
|
||||||
|
formControlName="from"
|
||||||
|
placeholder="dd/mm/yy"
|
||||||
|
/>
|
||||||
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
<mat-datepicker-toggle [for]="fromPicker" matSuffix>
|
||||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
<mat-icon
|
||||||
|
matDatepickerToggleIcon
|
||||||
|
svgIcon="red:calendar"
|
||||||
|
></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
<mat-datepicker #fromPicker></mat-datepicker>
|
<mat-datepicker #fromPicker></mat-datepicker>
|
||||||
</div>
|
</div>
|
||||||
@ -76,9 +95,16 @@
|
|||||||
<div class="mr-20" translate="audit-screen.to"></div>
|
<div class="mr-20" translate="audit-screen.to"></div>
|
||||||
|
|
||||||
<div class="red-input-group datepicker-wrapper">
|
<div class="red-input-group datepicker-wrapper">
|
||||||
<input [matDatepicker]="toPicker" formControlName="to" placeholder="dd/mm/yy" />
|
<input
|
||||||
|
[matDatepicker]="toPicker"
|
||||||
|
formControlName="to"
|
||||||
|
placeholder="dd/mm/yy"
|
||||||
|
/>
|
||||||
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
<mat-datepicker-toggle [for]="toPicker" matSuffix>
|
||||||
<mat-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
<mat-icon
|
||||||
|
matDatepickerToggleIcon
|
||||||
|
svgIcon="red:calendar"
|
||||||
|
></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
<mat-datepicker #toPicker></mat-datepicker>
|
<mat-datepicker #toPicker></mat-datepicker>
|
||||||
</div>
|
</div>
|
||||||
@ -87,14 +113,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-header" redactionSyncWidth="table-item">
|
<div class="table-header" redactionSyncWidth="table-item">
|
||||||
<redaction-table-col-name column="message" label="audit-screen.table-col-names.message"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
<redaction-table-col-name column="date" label="audit-screen.table-col-names.date"></redaction-table-col-name>
|
column="message"
|
||||||
<redaction-table-col-name class="user-column" column="user" label="audit-screen.table-col-names.user"></redaction-table-col-name>
|
label="audit-screen.table-col-names.message"
|
||||||
<redaction-table-col-name column="category" label="audit-screen.table-col-names.category"></redaction-table-col-name>
|
></redaction-table-col-name>
|
||||||
|
<redaction-table-col-name
|
||||||
|
column="date"
|
||||||
|
label="audit-screen.table-col-names.date"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
<redaction-table-col-name
|
||||||
|
class="user-column"
|
||||||
|
column="user"
|
||||||
|
label="audit-screen.table-col-names.user"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
<redaction-table-col-name
|
||||||
|
column="category"
|
||||||
|
label="audit-screen.table-col-names.category"
|
||||||
|
></redaction-table-col-name>
|
||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="!logs?.totalHits" icon="red:document" screen="audit-screen"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="!logs?.totalHits"
|
||||||
|
icon="red:document"
|
||||||
|
screen="audit-screen"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<div *cdkVirtualFor="let log of logs?.data" class="table-item">
|
<div *cdkVirtualFor="let log of logs?.data" class="table-item">
|
||||||
@ -105,7 +148,11 @@
|
|||||||
{{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }}
|
{{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="user-column">
|
<div class="user-column">
|
||||||
<redaction-initials-avatar [userId]="log.userId" [withName]="true" size="small"></redaction-initials-avatar>
|
<redaction-initials-avatar
|
||||||
|
[userId]="log.userId"
|
||||||
|
[withName]="true"
|
||||||
|
size="small"
|
||||||
|
></redaction-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ log.category }}
|
{{ log.category }}
|
||||||
@ -118,4 +165,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -61,7 +61,16 @@ export class AuditScreenComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _updateDateFilters(value): boolean {
|
private _updateDateFilters(value): boolean {
|
||||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) {
|
if (
|
||||||
|
applyIntervalConstraints(
|
||||||
|
value,
|
||||||
|
this._previousFrom,
|
||||||
|
this._previousTo,
|
||||||
|
this.filterForm,
|
||||||
|
'from',
|
||||||
|
'to'
|
||||||
|
)
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,12 @@
|
|||||||
<div class="flex-1 actions">
|
<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]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
icon="red:close"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -17,7 +22,10 @@
|
|||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="header-item">
|
<div class="header-item">
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'default-colors-screen.table-header.title' | translate: { length: allEntities.length } }}
|
{{
|
||||||
|
'default-colors-screen.table-header.title'
|
||||||
|
| translate: { length: allEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -30,7 +38,10 @@
|
|||||||
label="default-colors-screen.table-col-names.key"
|
label="default-colors-screen.table-col-names.key"
|
||||||
></redaction-table-col-name>
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name class="flex-center" label="default-colors-screen.table-col-names.color"></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></div>
|
||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
@ -38,13 +49,24 @@
|
|||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<!-- Table lines -->
|
<!-- Table lines -->
|
||||||
<div *cdkVirtualFor="let color of allEntities | sortBy: sortingOption.order:sortingOption.column" class="table-item">
|
<div
|
||||||
|
*cdkVirtualFor="
|
||||||
|
let color of allEntities | sortBy: sortingOption.order:sortingOption.column
|
||||||
|
"
|
||||||
|
class="table-item"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<div [translate]="'default-colors-screen.types.' + color.key" class="table-item-title heading"></div>
|
<div
|
||||||
|
[translate]="'default-colors-screen.types.' + color.key"
|
||||||
|
class="table-item-title heading"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="color-wrapper">
|
<div class="color-wrapper">
|
||||||
<div [ngStyle]="{ 'background-color': color.value }" class="color-square"></div>
|
<div
|
||||||
|
[ngStyle]="{ 'background-color': color.value }"
|
||||||
|
class="color-square"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
@ -66,4 +88,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import { BaseListingComponent } from '@shared/base/base-listing.component';
|
|||||||
templateUrl: './default-colors-screen.component.html',
|
templateUrl: './default-colors-screen.component.html',
|
||||||
styleUrls: ['./default-colors-screen.component.scss']
|
styleUrls: ['./default-colors-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: string; value: string }> {
|
export class DefaultColorsScreenComponent extends BaseListingComponent<{
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}> {
|
||||||
viewReady = false;
|
viewReady = false;
|
||||||
protected readonly _sortKey = 'default-colors';
|
protected readonly _sortKey = 'default-colors';
|
||||||
private _colorsObj: Colors;
|
private _colorsObj: Colors;
|
||||||
@ -35,9 +38,14 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st
|
|||||||
|
|
||||||
openEditColorDialog($event: any, color: { key: string; value: string }) {
|
openEditColorDialog($event: any, color: { key: string; value: string }) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => {
|
this._dialogService.openEditColorsDialog(
|
||||||
this._loadColors();
|
this._colorsObj,
|
||||||
});
|
color.key,
|
||||||
|
this._appStateService.activeRuleSetId,
|
||||||
|
async () => {
|
||||||
|
this._loadColors();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadColors() {
|
private _loadColors() {
|
||||||
|
|||||||
@ -5,7 +5,12 @@
|
|||||||
<div class="flex-1 actions">
|
<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]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
icon="red:close"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -25,11 +30,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
{{
|
||||||
|
'dictionary-listing.table-header.title'
|
||||||
|
| translate: { length: displayedEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="attributes-actions-container">
|
<div class="attributes-actions-container">
|
||||||
<redaction-search-input [form]="searchForm" [placeholder]="'dictionary-listing.search'"></redaction-search-input>
|
<redaction-search-input
|
||||||
|
[form]="searchForm"
|
||||||
|
[placeholder]="'dictionary-listing.search'"
|
||||||
|
></redaction-search-input>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<redaction-icon-button
|
<redaction-icon-button
|
||||||
(action)="openAddEditDictionaryDialog()"
|
(action)="openAddEditDictionaryDialog()"
|
||||||
@ -42,7 +53,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.no-data]="!allEntities.length" class="table-header" redactionSyncWidth="table-item">
|
<div
|
||||||
|
[class.no-data]="!allEntities.length"
|
||||||
|
class="table-header"
|
||||||
|
redactionSyncWidth="table-item"
|
||||||
|
>
|
||||||
<div class="select-oval-placeholder"></div>
|
<div class="select-oval-placeholder"></div>
|
||||||
|
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
@ -62,7 +77,10 @@
|
|||||||
label="dictionary-listing.table-col-names.order-of-importance"
|
label="dictionary-listing.table-col-names.order-of-importance"
|
||||||
></redaction-table-col-name>
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name class="flex-center" label="dictionary-listing.table-col-names.hint-redaction"></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></div>
|
||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -75,20 +93,32 @@
|
|||||||
screen="dictionary-listing"
|
screen="dictionary-listing"
|
||||||
></redaction-empty-state>
|
></redaction-empty-state>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="allEntities.length && !displayedEntities.length" screen="dictionary-listing" type="no-match"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="allEntities.length && !displayedEntities.length"
|
||||||
|
screen="dictionary-listing"
|
||||||
|
type="no-match"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<div
|
<div
|
||||||
*cdkVirtualFor="let dict of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
|
*cdkVirtualFor="
|
||||||
|
let dict of displayedEntities
|
||||||
|
| sortBy: sortingOption.order:sortingOption.column
|
||||||
|
"
|
||||||
[routerLink]="[dict.type]"
|
[routerLink]="[dict.type]"
|
||||||
class="table-item pointer"
|
class="table-item pointer"
|
||||||
>
|
>
|
||||||
<div (click)="toggleEntitySelected($event, dict)" class="selection-column">
|
<div (click)="toggleEntitySelected($event, dict)" class="selection-column">
|
||||||
<redaction-round-checkbox [active]="isEntitySelected(dict)"></redaction-round-checkbox>
|
<redaction-round-checkbox
|
||||||
|
[active]="isEntitySelected(dict)"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div [ngStyle]="{ 'background-color': dict.hexColor }" class="color-square"></div>
|
<div
|
||||||
|
[ngStyle]="{ 'background-color': dict.hexColor }"
|
||||||
|
class="color-square"
|
||||||
|
></div>
|
||||||
<div class="dict-name">
|
<div class="dict-name">
|
||||||
<div class="table-item-title heading">
|
<div class="table-item-title heading">
|
||||||
{{ dict.label }}
|
{{ dict.label }}
|
||||||
@ -111,7 +141,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<redaction-annotation-icon [dictType]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon>
|
<redaction-annotation-icon
|
||||||
|
[dictType]="dict"
|
||||||
|
[type]="dict.hint ? 'circle' : 'square'"
|
||||||
|
></redaction-annotation-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
@ -154,4 +187,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -14,7 +14,10 @@ import { BaseListingComponent } from '@shared/base/base-listing.component';
|
|||||||
templateUrl: './dictionary-listing-screen.component.html',
|
templateUrl: './dictionary-listing-screen.component.html',
|
||||||
styleUrls: ['./dictionary-listing-screen.component.scss']
|
styleUrls: ['./dictionary-listing-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValue> implements OnInit {
|
export class DictionaryListingScreenComponent
|
||||||
|
extends BaseListingComponent<TypeValue>
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
viewReady = false;
|
viewReady = false;
|
||||||
chartData: DoughnutChartConfig[] = [];
|
chartData: DoughnutChartConfig[] = [];
|
||||||
protected readonly _searchKey = 'label';
|
protected readonly _searchKey = 'label';
|
||||||
@ -39,38 +42,50 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
|
|||||||
|
|
||||||
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
|
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
|
||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
|
this._dialogService.openAddEditDictionaryDialog(
|
||||||
if (newDictionary) {
|
dict,
|
||||||
await this._appStateService.loadDictionaryData();
|
this._appStateService.activeRuleSetId,
|
||||||
this._loadDictionaryData();
|
async (newDictionary) => {
|
||||||
|
if (newDictionary) {
|
||||||
|
await this._appStateService.loadDictionaryData();
|
||||||
|
this._loadDictionaryData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
|
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
|
||||||
this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
|
this._dialogService.openDeleteDictionaryDialog(
|
||||||
await this._appStateService.loadDictionaryData();
|
$event,
|
||||||
this._loadDictionaryData();
|
dict,
|
||||||
});
|
this._appStateService.activeRuleSetId,
|
||||||
|
async () => {
|
||||||
|
await this._appStateService.loadDictionaryData();
|
||||||
|
this._loadDictionaryData();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadDictionaryData() {
|
private _loadDictionaryData() {
|
||||||
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
|
const appStateDictionaryData =
|
||||||
|
this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
|
||||||
this.allEntities = Object.keys(appStateDictionaryData)
|
this.allEntities = Object.keys(appStateDictionaryData)
|
||||||
.map((key) => appStateDictionaryData[key])
|
.map((key) => appStateDictionaryData[key])
|
||||||
.filter((d) => !d.virtual);
|
.filter((d) => !d.virtual);
|
||||||
this.displayedEntities = [...this.allEntities];
|
this.displayedEntities = [...this.allEntities];
|
||||||
const dataObs = this.allEntities.map((dict) =>
|
const dataObs = this.allEntities.map((dict) =>
|
||||||
this._dictionaryControllerService.getDictionaryForType(this._appStateService.activeRuleSetId, dict.type).pipe(
|
this._dictionaryControllerService
|
||||||
tap((values) => {
|
.getDictionaryForType(this._appStateService.activeRuleSetId, dict.type)
|
||||||
dict.entries = values.entries ? values.entries : [];
|
.pipe(
|
||||||
}),
|
tap((values) => {
|
||||||
catchError(() => {
|
dict.entries = values.entries ? values.entries : [];
|
||||||
console.log('error');
|
}),
|
||||||
dict.entries = [];
|
catchError(() => {
|
||||||
return of({});
|
console.log('error');
|
||||||
})
|
dict.entries = [];
|
||||||
)
|
return of({});
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
forkJoin(dataObs)
|
forkJoin(dataObs)
|
||||||
.pipe(defaultIfEmpty(null))
|
.pipe(defaultIfEmpty(null))
|
||||||
|
|||||||
@ -38,7 +38,14 @@
|
|||||||
tooltipPosition="below"
|
tooltipPosition="below"
|
||||||
></redaction-circle-button>
|
></redaction-circle-button>
|
||||||
|
|
||||||
<input #fileInput (change)="upload($event)" accept="text/plain" class="file-upload-input" hidden type="file" />
|
<input
|
||||||
|
#fileInput
|
||||||
|
(change)="upload($event)"
|
||||||
|
accept="text/plain"
|
||||||
|
class="file-upload-input"
|
||||||
|
hidden
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
[routerLink]="['..']"
|
[routerLink]="['..']"
|
||||||
@ -93,11 +100,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!!dictionary.description" class="pb-32 mt-20">
|
<div *ngIf="!!dictionary.description" class="pb-32 mt-20">
|
||||||
<div class="heading" translate="dictionary-overview.dictionary-details.description"></div>
|
<div
|
||||||
|
class="heading"
|
||||||
|
translate="dictionary-overview.dictionary-details.description"
|
||||||
|
></div>
|
||||||
<div class="mt-8">{{ dictionary.description }}</div>
|
<div class="mt-8">{{ dictionary.description }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="processing"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="processing"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -21,7 +21,8 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
processing = false;
|
processing = false;
|
||||||
entries: string[] = [];
|
entries: string[] = [];
|
||||||
|
|
||||||
@ViewChild('dictionaryManager', { static: false }) private _dictionaryManager: DictionaryManagerComponent;
|
@ViewChild('dictionaryManager', { static: false })
|
||||||
|
private _dictionaryManager: DictionaryManagerComponent;
|
||||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -37,7 +38,10 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
private readonly _formBuilder: FormBuilder
|
private readonly _formBuilder: FormBuilder
|
||||||
) {
|
) {
|
||||||
super(_translateService);
|
super(_translateService);
|
||||||
this._appStateService.activateDictionary(this._activatedRoute.snapshot.params.type, this._activatedRoute.snapshot.params.ruleSetId);
|
this._appStateService.activateDictionary(
|
||||||
|
this._activatedRoute.snapshot.params.type,
|
||||||
|
this._activatedRoute.snapshot.params.ruleSetId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -54,16 +58,25 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
|
|
||||||
openEditDictionaryDialog($event: any) {
|
openEditDictionaryDialog($event: any) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._dialogService.openAddEditDictionaryDialog(this.dictionary, this.dictionary.ruleSetId, async () => {
|
this._dialogService.openAddEditDictionaryDialog(
|
||||||
await this._appStateService.loadDictionaryData();
|
this.dictionary,
|
||||||
});
|
this.dictionary.ruleSetId,
|
||||||
|
async () => {
|
||||||
|
await this._appStateService.loadDictionaryData();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
openDeleteDictionaryDialog($event: any) {
|
openDeleteDictionaryDialog($event: any) {
|
||||||
this._dialogService.openDeleteDictionaryDialog($event, this.dictionary, this.dictionary.ruleSetId, async () => {
|
this._dialogService.openDeleteDictionaryDialog(
|
||||||
await this._appStateService.loadDictionaryData();
|
$event,
|
||||||
this._router.navigate(['..']);
|
this.dictionary,
|
||||||
});
|
this.dictionary.ruleSetId,
|
||||||
|
async () => {
|
||||||
|
await this._appStateService.loadDictionaryData();
|
||||||
|
this._router.navigate(['..']);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
download(): void {
|
download(): void {
|
||||||
@ -89,28 +102,40 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
|
|
||||||
saveEntries(entries: string[]) {
|
saveEntries(entries: string[]) {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this._dictionarySaveService.saveEntries(entries, this.entries, this.dictionary.ruleSetId, this.dictionary.type, null).subscribe(
|
this._dictionarySaveService
|
||||||
() => {
|
.saveEntries(
|
||||||
this.processing = false;
|
entries,
|
||||||
this._loadEntries();
|
this.entries,
|
||||||
},
|
this.dictionary.ruleSetId,
|
||||||
() => {
|
this.dictionary.type,
|
||||||
this.processing = false;
|
null
|
||||||
}
|
)
|
||||||
);
|
.subscribe(
|
||||||
|
() => {
|
||||||
|
this.processing = false;
|
||||||
|
this._loadEntries();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.processing = false;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadEntries() {
|
private _loadEntries() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
this._dictionaryControllerService.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type).subscribe(
|
this._dictionaryControllerService
|
||||||
(data) => {
|
.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type)
|
||||||
this.processing = false;
|
.subscribe(
|
||||||
this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
(data) => {
|
||||||
},
|
this.processing = false;
|
||||||
() => {
|
this.entries = data.entries.sort((str1, str2) =>
|
||||||
this.processing = false;
|
str1.localeCompare(str2, undefined, { sensitivity: 'accent' })
|
||||||
this.entries = [];
|
);
|
||||||
}
|
},
|
||||||
);
|
() => {
|
||||||
|
this.processing = false;
|
||||||
|
this.entries = [];
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,19 @@
|
|||||||
<div class="red-content-inner">
|
<div class="red-content-inner">
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
<div class="content-container-content">
|
<div class="content-container-content">
|
||||||
<form (keyup)="formChanged()" *ngIf="digitalSignatureForm" [formGroup]="digitalSignatureForm" autocomplete="off">
|
<form
|
||||||
<input #fileInput (change)="fileChanged($event, fileInput)" class="file-upload-input" hidden type="file" />
|
(keyup)="formChanged()"
|
||||||
|
*ngIf="digitalSignatureForm"
|
||||||
|
[formGroup]="digitalSignatureForm"
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
#fileInput
|
||||||
|
(change)="fileChanged($event, fileInput)"
|
||||||
|
class="file-upload-input"
|
||||||
|
hidden
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
|
||||||
<redaction-empty-state
|
<redaction-empty-state
|
||||||
(action)="fileInput.click()"
|
(action)="fileInput.click()"
|
||||||
@ -32,34 +43,66 @@
|
|||||||
screen="digital-signature-screen"
|
screen="digital-signature-screen"
|
||||||
></redaction-empty-state>
|
></redaction-empty-state>
|
||||||
|
|
||||||
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group required w-300">
|
<div
|
||||||
<label translate="digital-signature-screen.certificate-name.label"></label>
|
[class.hidden]="!hasDigitalSignatureSet"
|
||||||
|
class="red-input-group required w-300"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
translate="digital-signature-screen.certificate-name.label"
|
||||||
|
></label>
|
||||||
<input
|
<input
|
||||||
[placeholder]="'digital-signature-screen.certificate-name.placeholder' | translate"
|
[placeholder]="
|
||||||
|
'digital-signature-screen.certificate-name.placeholder'
|
||||||
|
| translate
|
||||||
|
"
|
||||||
formControlName="certificateName"
|
formControlName="certificateName"
|
||||||
name="certificateName"
|
name="certificateName"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!digitalSignatureExists" [class.hidden]="!hasDigitalSignatureSet" class="red-input-group required w-300">
|
<div
|
||||||
|
*ngIf="!digitalSignatureExists"
|
||||||
|
[class.hidden]="!hasDigitalSignatureSet"
|
||||||
|
class="red-input-group required w-300"
|
||||||
|
>
|
||||||
<label translate="digital-signature-screen.password.label"></label>
|
<label translate="digital-signature-screen.password.label"></label>
|
||||||
<input [placeholder]="'digital-signature-screen.password.placeholder' | translate" formControlName="keySecret" name="keySecret" />
|
<input
|
||||||
|
[placeholder]="
|
||||||
|
'digital-signature-screen.password.placeholder' | translate
|
||||||
|
"
|
||||||
|
formControlName="keySecret"
|
||||||
|
name="keySecret"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||||
<label translate="digital-signature-screen.reason.label"></label>
|
<label translate="digital-signature-screen.reason.label"></label>
|
||||||
<input [placeholder]="'digital-signature-screen.reason.placeholder' | translate" formControlName="reason" name="reason" />
|
<input
|
||||||
|
[placeholder]="
|
||||||
|
'digital-signature-screen.reason.placeholder' | translate
|
||||||
|
"
|
||||||
|
formControlName="reason"
|
||||||
|
name="reason"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||||
<label translate="digital-signature-screen.location.label"></label>
|
<label translate="digital-signature-screen.location.label"></label>
|
||||||
<input [placeholder]="'digital-signature-screen.location.placeholder' | translate" formControlName="location" name="location" />
|
<input
|
||||||
|
[placeholder]="
|
||||||
|
'digital-signature-screen.location.placeholder' | translate
|
||||||
|
"
|
||||||
|
formControlName="location"
|
||||||
|
name="location"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||||
<label translate="digital-signature-screen.contact-info.label"></label>
|
<label translate="digital-signature-screen.contact-info.label"></label>
|
||||||
<input
|
<input
|
||||||
[placeholder]="'digital-signature-screen.contact-info.placeholder' | translate"
|
[placeholder]="
|
||||||
|
'digital-signature-screen.contact-info.placeholder' | translate
|
||||||
|
"
|
||||||
formControlName="contactInfo"
|
formControlName="contactInfo"
|
||||||
name="contactInfo"
|
name="contactInfo"
|
||||||
/>
|
/>
|
||||||
@ -96,4 +139,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -29,7 +29,10 @@ export class DigitalSignatureScreenComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasDigitalSignatureSet() {
|
get hasDigitalSignatureSet() {
|
||||||
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
|
return (
|
||||||
|
this.digitalSignatureExists ||
|
||||||
|
!!this.digitalSignatureForm.get('base64EncodedPrivateKey').value
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveDigitalSignature() {
|
saveDigitalSignature() {
|
||||||
@ -55,13 +58,17 @@ export class DigitalSignatureScreenComponent {
|
|||||||
(error) => {
|
(error) => {
|
||||||
if (error.status === 400) {
|
if (error.status === 400) {
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('digital-signature-screen.action.certificate-not-valid-error'),
|
this._translateService.instant(
|
||||||
|
'digital-signature-screen.action.certificate-not-valid-error'
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
NotificationType.ERROR
|
NotificationType.ERROR
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('digital-signature-screen.action.save-error'),
|
this._translateService.instant(
|
||||||
|
'digital-signature-screen.action.save-error'
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
NotificationType.ERROR
|
NotificationType.ERROR
|
||||||
);
|
);
|
||||||
@ -75,7 +82,9 @@ export class DigitalSignatureScreenComponent {
|
|||||||
() => {
|
() => {
|
||||||
this.loadDigitalSignatureAndInitializeForm();
|
this.loadDigitalSignatureAndInitializeForm();
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('digital-signature-screen.action.delete-success'),
|
this._translateService.instant(
|
||||||
|
'digital-signature-screen.action.delete-success'
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
NotificationType.SUCCESS
|
NotificationType.SUCCESS
|
||||||
);
|
);
|
||||||
@ -130,9 +139,13 @@ export class DigitalSignatureScreenComponent {
|
|||||||
certificateName: [this.digitalSignature.certificateName, Validators.required],
|
certificateName: [this.digitalSignature.certificateName, Validators.required],
|
||||||
contactInfo: this.digitalSignature.contactInfo,
|
contactInfo: this.digitalSignature.contactInfo,
|
||||||
location: this.digitalSignature.location,
|
location: this.digitalSignature.location,
|
||||||
keySecret: this.digitalSignatureExists ? null : [this.digitalSignature.password, Validators.required],
|
keySecret: this.digitalSignatureExists
|
||||||
|
? null
|
||||||
|
: [this.digitalSignature.password, Validators.required],
|
||||||
reason: this.digitalSignature.reason,
|
reason: this.digitalSignature.reason,
|
||||||
base64EncodedPrivateKey: this.digitalSignatureExists ? null : [this.digitalSignature.base64EncodedPrivateKey, Validators.required]
|
base64EncodedPrivateKey: this.digitalSignatureExists
|
||||||
|
? null
|
||||||
|
: [this.digitalSignature.base64EncodedPrivateKey, Validators.required]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,12 @@
|
|||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||||
|
|
||||||
<div class="actions flex-1">
|
<div class="actions flex-1">
|
||||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
icon="red:close"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -23,7 +28,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'file-attributes-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
{{
|
||||||
|
'file-attributes-listing.table-header.title'
|
||||||
|
| translate: { length: displayedEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
@ -38,8 +46,17 @@
|
|||||||
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
|
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
|
||||||
|
|
||||||
<div class="attributes-actions-container">
|
<div class="attributes-actions-container">
|
||||||
<redaction-search-input [form]="searchForm" [placeholder]="'file-attributes-listing.search'"></redaction-search-input>
|
<redaction-search-input
|
||||||
<input #fileInput (change)="importCSV($event.target['files'])" accept=".csv" class="csv-input" type="file" />
|
[form]="searchForm"
|
||||||
|
[placeholder]="'file-attributes-listing.search'"
|
||||||
|
></redaction-search-input>
|
||||||
|
<input
|
||||||
|
#fileInput
|
||||||
|
(change)="importCSV($event.target['files'])"
|
||||||
|
accept=".csv"
|
||||||
|
class="csv-input"
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="fileInput.click()"
|
(action)="fileInput.click()"
|
||||||
@ -58,7 +75,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.no-data]="!allEntities.length" class="table-header" redactionSyncWidth="table-item">
|
<div
|
||||||
|
[class.no-data]="!allEntities.length"
|
||||||
|
class="table-header"
|
||||||
|
redactionSyncWidth="table-item"
|
||||||
|
>
|
||||||
<div class="select-oval-placeholder"></div>
|
<div class="select-oval-placeholder"></div>
|
||||||
|
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
@ -86,7 +107,9 @@
|
|||||||
label="file-attributes-listing.table-col-names.read-only"
|
label="file-attributes-listing.table-col-names.read-only"
|
||||||
></redaction-table-col-name>
|
></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
|
||||||
|
label="file-attributes-listing.table-col-names.csv-column"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
class="flex-center"
|
class="flex-center"
|
||||||
@ -100,7 +123,11 @@
|
|||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:attribute" screen="file-attributes-listing"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="!allEntities.length"
|
||||||
|
icon="red:attribute"
|
||||||
|
screen="file-attributes-listing"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<redaction-empty-state
|
<redaction-empty-state
|
||||||
*ngIf="allEntities.length && !displayedEntities.length"
|
*ngIf="allEntities.length && !displayedEntities.length"
|
||||||
@ -110,16 +137,27 @@
|
|||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<!-- Table lines -->
|
<!-- Table lines -->
|
||||||
<div *cdkVirtualFor="let attribute of displayedEntities | sortBy: sortingOption.order:sortingOption.column" class="table-item">
|
<div
|
||||||
|
*cdkVirtualFor="
|
||||||
|
let attribute of displayedEntities
|
||||||
|
| sortBy: sortingOption.order:sortingOption.column
|
||||||
|
"
|
||||||
|
class="table-item"
|
||||||
|
>
|
||||||
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
|
<div (click)="toggleEntitySelected($event, attribute)" class="selection-column">
|
||||||
<redaction-round-checkbox [active]="isEntitySelected(attribute)"></redaction-round-checkbox>
|
<redaction-round-checkbox
|
||||||
|
[active]="isEntitySelected(attribute)"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label">
|
<div class="label">
|
||||||
<span>{{ attribute.label }}</span>
|
<span>{{ attribute.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [translate]="'file-attribute-types.' + attribute.type" class="small-label"></div>
|
<div
|
||||||
|
[translate]="'file-attribute-types.' + attribute.type"
|
||||||
|
class="small-label"
|
||||||
|
></div>
|
||||||
|
|
||||||
<div class="center read-only">
|
<div class="center read-only">
|
||||||
<mat-icon
|
<mat-icon
|
||||||
@ -133,7 +171,11 @@
|
|||||||
{{ attribute.csvColumnHeader }}
|
{{ attribute.csvColumnHeader }}
|
||||||
</div>
|
</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<redaction-round-checkbox *ngIf="attribute.primaryAttribute" [active]="true" [size]="18"></redaction-round-checkbox>
|
<redaction-round-checkbox
|
||||||
|
*ngIf="attribute.primaryAttribute"
|
||||||
|
[active]="true"
|
||||||
|
[size]="18"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
@ -162,4 +204,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core';
|
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 {
|
||||||
|
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 { ActivatedRoute } from '@angular/router';
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
@ -11,7 +15,10 @@ import { BaseListingComponent } from '@shared/base/base-listing.component';
|
|||||||
templateUrl: './file-attributes-listing-screen.component.html',
|
templateUrl: './file-attributes-listing-screen.component.html',
|
||||||
styleUrls: ['./file-attributes-listing-screen.component.scss']
|
styleUrls: ['./file-attributes-listing-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class FileAttributesListingScreenComponent extends BaseListingComponent<FileAttributeConfig> implements OnInit {
|
export class FileAttributesListingScreenComponent
|
||||||
|
extends BaseListingComponent<FileAttributeConfig>
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
viewReady = false;
|
viewReady = false;
|
||||||
loading = false;
|
loading = false;
|
||||||
protected readonly _searchKey = 'label';
|
protected readonly _searchKey = 'label';
|
||||||
@ -39,38 +46,65 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
|
|||||||
|
|
||||||
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async (newValue: FileAttributeConfig) => {
|
this._dialogService.openAddEditFileAttributeDialog(
|
||||||
this.loading = true;
|
fileAttribute,
|
||||||
await this._fileAttributesService.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId).toPromise();
|
this._appStateService.activeRuleSetId,
|
||||||
await this._loadData();
|
async (newValue: FileAttributeConfig) => {
|
||||||
});
|
this.loading = true;
|
||||||
|
await this._fileAttributesService
|
||||||
|
.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId)
|
||||||
|
.toPromise();
|
||||||
|
await this._loadData();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._dialogService.openConfirmDeleteFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
|
this._dialogService.openConfirmDeleteFileAttributeDialog(
|
||||||
this.loading = true;
|
fileAttribute,
|
||||||
if (fileAttribute) {
|
this._appStateService.activeRuleSetId,
|
||||||
await this._fileAttributesService.deleteFileAttribute(this._appStateService.activeRuleSetId, fileAttribute.id).toPromise();
|
async () => {
|
||||||
} else {
|
this.loading = true;
|
||||||
await this._fileAttributesService.deleteFileAttributes(this.selectedEntitiesIds, this._appStateService.activeRuleSetId).toPromise();
|
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();
|
||||||
}
|
}
|
||||||
await this._loadData();
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
importCSV(files: FileList | File[]) {
|
importCSV(files: FileList | File[]) {
|
||||||
const csvFile = files[0];
|
const csvFile = files[0];
|
||||||
this._fileInput.nativeElement.value = null;
|
this._fileInput.nativeElement.value = null;
|
||||||
|
|
||||||
this._dialogService.openImportFileAttributeCSVDialog(csvFile, this._appStateService.activeRuleSetId, this._existingConfiguration, async () => {
|
this._dialogService.openImportFileAttributeCSVDialog(
|
||||||
await this._loadData();
|
csvFile,
|
||||||
});
|
this._appStateService.activeRuleSetId,
|
||||||
|
this._existingConfiguration,
|
||||||
|
async () => {
|
||||||
|
await this._loadData();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
try {
|
try {
|
||||||
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
|
const response = await this._fileAttributesService
|
||||||
|
.getFileAttributesConfiguration(this._appStateService.activeRuleSetId)
|
||||||
|
.toPromise();
|
||||||
this._existingConfiguration = response;
|
this._existingConfiguration = response;
|
||||||
this.allEntities = response?.fileAttributeConfigs || [];
|
this.allEntities = response?.fileAttributeConfigs || [];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -8,7 +8,12 @@
|
|||||||
<div class="breadcrumb" translate="license-information"></div>
|
<div class="breadcrumb" translate="license-information"></div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button (click)="sendMail()" color="primary" mat-flat-button translate="license-info-screen.email-report"></button>
|
<button
|
||||||
|
(click)="sendMail()"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
translate="license-info-screen.email-report"
|
||||||
|
></button>
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
*ngIf="permissionsService.isUser()"
|
*ngIf="permissionsService.isUser()"
|
||||||
class="ml-6"
|
class="ml-6"
|
||||||
@ -40,7 +45,12 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div translate="license-info-screen.copyright-claim-title"></div>
|
<div translate="license-info-screen.copyright-claim-title"></div>
|
||||||
<div>{{ 'license-info-screen.copyright-claim-text' | translate: { currentYear: currentYear } }}</div>
|
<div>
|
||||||
|
{{
|
||||||
|
'license-info-screen.copyright-claim-text'
|
||||||
|
| translate: { currentYear: currentYear }
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -54,7 +64,10 @@
|
|||||||
<!-- <div>Future feature: we will provide that with the /info endpoint</div>-->
|
<!-- <div>Future feature: we will provide that with the /info endpoint</div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<div class="section-title all-caps-label" translate="license-info-screen.licensing-details"></div>
|
<div
|
||||||
|
class="section-title all-caps-label"
|
||||||
|
translate="license-info-screen.licensing-details"
|
||||||
|
></div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div translate="license-info-screen.licensed-to"></div>
|
<div translate="license-info-screen.licensed-to"></div>
|
||||||
@ -63,7 +76,10 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div translate="license-info-screen.licensing-period"></div>
|
<div translate="license-info-screen.licensing-period"></div>
|
||||||
<div>{{ appConfigService.getConfig('LICENSE_START', '-') }} / {{ appConfigService.getConfig('LICENSE_END', '-') }}</div>
|
<div>
|
||||||
|
{{ appConfigService.getConfig('LICENSE_START', '-') }} /
|
||||||
|
{{ appConfigService.getConfig('LICENSE_END', '-') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -76,16 +92,28 @@
|
|||||||
<div>{{ currentInfo.numberOfAnalyzedPages }}</div>
|
<div>{{ currentInfo.numberOfAnalyzedPages }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="section-title all-caps-label" translate="license-info-screen.usage-details"></div>
|
<div
|
||||||
|
class="section-title all-caps-label"
|
||||||
|
translate="license-info-screen.usage-details"
|
||||||
|
></div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div>{{ 'license-info-screen.total-analyzed' | translate: { date: totalInfo.startDate | date: 'longDate' } }}</div>
|
<div>
|
||||||
|
{{
|
||||||
|
'license-info-screen.total-analyzed'
|
||||||
|
| translate: { date: totalInfo.startDate | date: 'longDate' }
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
<div>{{ totalInfo.numberOfAnalyzedPages }}</div>
|
<div>{{ totalInfo.numberOfAnalyzedPages }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div translate="license-info-screen.current-analyzed"></div>
|
<div translate="license-info-screen.current-analyzed"></div>
|
||||||
<div>{{ currentInfo.numberOfAnalyzedPages }} ({{ analysisPercentageOfLicense | number: '1.0-2' }}%)</div>
|
<div>
|
||||||
|
{{ currentInfo.numberOfAnalyzedPages }} ({{
|
||||||
|
analysisPercentageOfLicense | number: '1.0-2'
|
||||||
|
}}%)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="!!unlicensedInfo" class="row">
|
<div *ngIf="!!unlicensedInfo" class="row">
|
||||||
@ -123,4 +151,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -54,31 +54,47 @@ export class LicenseInformationScreenComponent implements OnInit {
|
|||||||
startDate: startDate.toDate(),
|
startDate: startDate.toDate(),
|
||||||
endDate: endDate.toDate()
|
endDate: endDate.toDate()
|
||||||
};
|
};
|
||||||
const promises = [this._licenseReportController.licenseReport(currentConfig).toPromise(), this._licenseReportController.licenseReport({}).toPromise()];
|
const promises = [
|
||||||
|
this._licenseReportController.licenseReport(currentConfig).toPromise(),
|
||||||
|
this._licenseReportController.licenseReport({}).toPromise()
|
||||||
|
];
|
||||||
|
|
||||||
if (endDate.isBefore(moment())) {
|
if (endDate.isBefore(moment())) {
|
||||||
const unlicensedConfig = {
|
const unlicensedConfig = {
|
||||||
startDate: endDate.toDate()
|
startDate: endDate.toDate()
|
||||||
};
|
};
|
||||||
promises.push(this._licenseReportController.licenseReport(unlicensedConfig).toPromise());
|
promises.push(
|
||||||
|
this._licenseReportController.licenseReport(unlicensedConfig).toPromise()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(promises).then((reports) => {
|
Promise.all(promises).then((reports) => {
|
||||||
[this.currentInfo, this.totalInfo, this.unlicensedInfo] = reports;
|
[this.currentInfo, this.totalInfo, this.unlicensedInfo] = reports;
|
||||||
this.viewReady = true;
|
this.viewReady = true;
|
||||||
this.analysisPercentageOfLicense =
|
this.analysisPercentageOfLicense =
|
||||||
this.totalLicensedNumberOfPages > 0 ? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) * 100 : 100;
|
this.totalLicensedNumberOfPages > 0
|
||||||
|
? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) *
|
||||||
|
100
|
||||||
|
: 100;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMail(): void {
|
sendMail(): void {
|
||||||
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
|
const licenseCustomer = this.appConfigService.getConfig('LICENSE_CUSTOMER');
|
||||||
const subject = this._translateService.instant('license-info-screen.email.title', { licenseCustomer });
|
const subject = this._translateService.instant('license-info-screen.email.title', {
|
||||||
|
licenseCustomer
|
||||||
|
});
|
||||||
const body = [
|
const body = [
|
||||||
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
|
this._translateService.instant('license-info-screen.email.body.analyzed', {
|
||||||
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
|
pages: this.currentInfo.numberOfAnalyzedPages
|
||||||
|
}),
|
||||||
|
this._translateService.instant('license-info-screen.email.body.licensed', {
|
||||||
|
pages: this.totalLicensedNumberOfPages
|
||||||
|
})
|
||||||
].join('%0D%0A');
|
].join('%0D%0A');
|
||||||
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
|
window.location.href = `mailto:${this.appConfigService.getConfig(
|
||||||
|
'LICENSE_EMAIL'
|
||||||
|
)}?subject=${subject}&body=${body}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
|
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
|
||||||
|
|||||||
@ -28,15 +28,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'project-templates-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
{{
|
||||||
|
'project-templates-listing.table-header.title'
|
||||||
|
| translate: { length: displayedEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<div class="actions flex-1">
|
<div class="actions flex-1">
|
||||||
<redaction-search-input [form]="searchForm" [placeholder]="'project-templates-listing.search'"></redaction-search-input>
|
<redaction-search-input
|
||||||
|
[form]="searchForm"
|
||||||
|
[placeholder]="'project-templates-listing.search'"
|
||||||
|
></redaction-search-input>
|
||||||
|
|
||||||
<redaction-icon-button
|
<redaction-icon-button
|
||||||
(action)="openAddRuleSetDialog()"
|
(action)="openAddRuleSetDialog()"
|
||||||
*ngIf="permissionsService.isAdmin() && userPreferenceService.areDevFeaturesEnabled"
|
*ngIf="
|
||||||
|
permissionsService.isAdmin() &&
|
||||||
|
userPreferenceService.areDevFeaturesEnabled
|
||||||
|
"
|
||||||
icon="red:plus"
|
icon="red:plus"
|
||||||
text="project-templates-listing.add-new"
|
text="project-templates-listing.add-new"
|
||||||
type="primary"
|
type="primary"
|
||||||
@ -44,7 +53,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.no-data]="!allEntities.length" class="table-header" redactionSyncWidth="table-item">
|
<div
|
||||||
|
[class.no-data]="!allEntities.length"
|
||||||
|
class="table-header"
|
||||||
|
redactionSyncWidth="table-item"
|
||||||
|
>
|
||||||
<div class="select-oval-placeholder"></div>
|
<div class="select-oval-placeholder"></div>
|
||||||
|
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
@ -54,7 +67,10 @@
|
|||||||
column="name"
|
column="name"
|
||||||
label="project-templates-listing.table-col-names.name"
|
label="project-templates-listing.table-col-names.name"
|
||||||
></redaction-table-col-name>
|
></redaction-table-col-name>
|
||||||
<redaction-table-col-name class="user-column" label="project-templates-listing.table-col-names.created-by"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
class="user-column"
|
||||||
|
label="project-templates-listing.table-col-names.created-by"
|
||||||
|
></redaction-table-col-name>
|
||||||
<redaction-table-col-name
|
<redaction-table-col-name
|
||||||
(toggleSort)="toggleSort($event)"
|
(toggleSort)="toggleSort($event)"
|
||||||
[activeSortingOption]="sortingOption"
|
[activeSortingOption]="sortingOption"
|
||||||
@ -72,7 +88,11 @@
|
|||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:template" screen="project-templates-listing"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="!allEntities.length"
|
||||||
|
icon="red:template"
|
||||||
|
screen="project-templates-listing"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<redaction-empty-state
|
<redaction-empty-state
|
||||||
*ngIf="allEntities.length && !displayedEntities.length"
|
*ngIf="allEntities.length && !displayedEntities.length"
|
||||||
@ -82,12 +102,20 @@
|
|||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
|
||||||
<div
|
<div
|
||||||
*cdkVirtualFor="let ruleSet of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
|
*cdkVirtualFor="
|
||||||
|
let ruleSet of displayedEntities
|
||||||
|
| sortBy: sortingOption.order:sortingOption.column
|
||||||
|
"
|
||||||
[routerLink]="[ruleSet.ruleSetId, 'dictionaries']"
|
[routerLink]="[ruleSet.ruleSetId, 'dictionaries']"
|
||||||
class="table-item pointer"
|
class="table-item pointer"
|
||||||
>
|
>
|
||||||
<div (click)="toggleEntitySelected($event, ruleSet)" class="selection-column">
|
<div
|
||||||
<redaction-round-checkbox [active]="isEntitySelected(ruleSet)"></redaction-round-checkbox>
|
(click)="toggleEntitySelected($event, ruleSet)"
|
||||||
|
class="selection-column"
|
||||||
|
>
|
||||||
|
<redaction-round-checkbox
|
||||||
|
[active]="isEntitySelected(ruleSet)"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -97,13 +125,19 @@
|
|||||||
<div class="small-label stats-subtitle">
|
<div class="small-label stats-subtitle">
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||||
{{ 'project-templates-listing.dictionaries' | translate: { length: ruleSet.dictionariesCount } }}
|
{{
|
||||||
|
'project-templates-listing.dictionaries'
|
||||||
|
| translate: { length: ruleSet.dictionariesCount }
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="user-column">
|
<div class="user-column">
|
||||||
<redaction-initials-avatar [userId]="ruleSet.createdBy" [withName]="true"></redaction-initials-avatar>
|
<redaction-initials-avatar
|
||||||
|
[userId]="ruleSet.createdBy"
|
||||||
|
[withName]="true"
|
||||||
|
></redaction-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="small-label">
|
<div class="small-label">
|
||||||
{{ ruleSet.dateAdded | date: 'd MMM. yyyy' }}
|
{{ ruleSet.dateAdded | date: 'd MMM. yyyy' }}
|
||||||
|
|||||||
@ -11,7 +11,10 @@ import { RuleSetModel } from '@redaction/red-ui-http';
|
|||||||
templateUrl: './rule-sets-listing-screen.component.html',
|
templateUrl: './rule-sets-listing-screen.component.html',
|
||||||
styleUrls: ['./rule-sets-listing-screen.component.scss']
|
styleUrls: ['./rule-sets-listing-screen.component.scss']
|
||||||
})
|
})
|
||||||
export class RuleSetsListingScreenComponent extends BaseListingComponent<RuleSetModel> implements OnInit {
|
export class RuleSetsListingScreenComponent
|
||||||
|
extends BaseListingComponent<RuleSetModel>
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
protected readonly _searchKey = 'name';
|
protected readonly _searchKey = 'name';
|
||||||
protected readonly _selectionKey = 'ruleSetId';
|
protected readonly _selectionKey = 'ruleSetId';
|
||||||
protected readonly _sortKey = 'rule-sets-listing';
|
protected readonly _sortKey = 'rule-sets-listing';
|
||||||
|
|||||||
@ -5,7 +5,12 @@
|
|||||||
<div class="flex-1 actions">
|
<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]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
icon="red:close"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -29,10 +34,21 @@
|
|||||||
</ace-editor>
|
</ace-editor>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" class="changes-box">
|
<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>
|
<redaction-icon-button
|
||||||
<div (click)="revert()" class="all-caps-label cancel" translate="rules-screen.revert-changes"></div>
|
(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>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="processing"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="processing"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -69,7 +69,12 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
|||||||
const entry = this.currentLines[i];
|
const entry = this.currentLines[i];
|
||||||
if (entry?.trim().length > 0) {
|
if (entry?.trim().length > 0) {
|
||||||
// only mark non-empty lines
|
// 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')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,7 +97,11 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.error.generic'), null, NotificationType.ERROR);
|
this._notificationService.showToastNotification(
|
||||||
|
this._translateService.instant('rules-screen.error.generic'),
|
||||||
|
null,
|
||||||
|
NotificationType.ERROR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,9 @@
|
|||||||
<input
|
<input
|
||||||
formControlName="host"
|
formControlName="host"
|
||||||
name="host"
|
name="host"
|
||||||
placeholder="{{ 'smtp-config-screen.form.host-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.host-placeholder' | translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -44,7 +46,9 @@
|
|||||||
<input
|
<input
|
||||||
formControlName="port"
|
formControlName="port"
|
||||||
name="port"
|
name="port"
|
||||||
placeholder="{{ 'smtp-config-screen.form.port-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.port-placeholder' | translate
|
||||||
|
}}"
|
||||||
type="number"
|
type="number"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -54,20 +58,30 @@
|
|||||||
<input
|
<input
|
||||||
formControlName="from"
|
formControlName="from"
|
||||||
name="from"
|
name="from"
|
||||||
placeholder="{{ 'smtp-config-screen.form.from-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.from-placeholder' | translate
|
||||||
|
}}"
|
||||||
type="email"
|
type="email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.from-display-name"></label>
|
<label
|
||||||
|
translate="smtp-config-screen.form.from-display-name"
|
||||||
|
></label>
|
||||||
<input
|
<input
|
||||||
formControlName="fromDisplayName"
|
formControlName="fromDisplayName"
|
||||||
name="fromDisplayName"
|
name="fromDisplayName"
|
||||||
placeholder="{{ 'smtp-config-screen.form.from-display-name-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.from-display-name-placeholder'
|
||||||
|
| translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<span class="hint" translate="smtp-config-screen.form.from-display-name-hint"></span>
|
<span
|
||||||
|
class="hint"
|
||||||
|
translate="smtp-config-screen.form.from-display-name-hint"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
@ -75,44 +89,69 @@
|
|||||||
<input
|
<input
|
||||||
formControlName="replyTo"
|
formControlName="replyTo"
|
||||||
name="replyTo"
|
name="replyTo"
|
||||||
placeholder="{{ 'smtp-config-screen.form.reply-to-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.reply-to-placeholder'
|
||||||
|
| translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.reply-to-display-name"></label>
|
<label
|
||||||
|
translate="smtp-config-screen.form.reply-to-display-name"
|
||||||
|
></label>
|
||||||
<input
|
<input
|
||||||
formControlName="replyToDisplayName"
|
formControlName="replyToDisplayName"
|
||||||
name="replyToDisplayName"
|
name="replyToDisplayName"
|
||||||
placeholder="{{ 'smtp-config-screen.form.reply-to-display-name-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.reply-to-display-name-placeholder'
|
||||||
|
| translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.envelope-from"></label>
|
<label
|
||||||
|
translate="smtp-config-screen.form.envelope-from"
|
||||||
|
></label>
|
||||||
<input
|
<input
|
||||||
formControlName="envelopeFrom"
|
formControlName="envelopeFrom"
|
||||||
name="envelopeFrom"
|
name="envelopeFrom"
|
||||||
placeholder="{{ 'smtp-config-screen.form.envelope-from-placeholder' | translate }}"
|
placeholder="{{
|
||||||
|
'smtp-config-screen.form.envelope-from-placeholder'
|
||||||
|
| translate
|
||||||
|
}}"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<span class="hint" translate="smtp-config-screen.form.envelope-from-hint"></span>
|
<span
|
||||||
|
class="hint"
|
||||||
|
translate="smtp-config-screen.form.envelope-from-hint"
|
||||||
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.ssl"></label>
|
<label translate="smtp-config-screen.form.ssl"></label>
|
||||||
<mat-slide-toggle color="primary" formControlName="ssl"></mat-slide-toggle>
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
formControlName="ssl"
|
||||||
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.starttls"></label>
|
<label translate="smtp-config-screen.form.starttls"></label>
|
||||||
<mat-slide-toggle color="primary" formControlName="starttls"></mat-slide-toggle>
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
formControlName="starttls"
|
||||||
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label translate="smtp-config-screen.form.auth"></label>
|
<label translate="smtp-config-screen.form.auth"></label>
|
||||||
<mat-slide-toggle color="primary" formControlName="auth"></mat-slide-toggle>
|
<mat-slide-toggle
|
||||||
|
color="primary"
|
||||||
|
formControlName="auth"
|
||||||
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
(click)="openAuthConfigDialog(true)"
|
(click)="openAuthConfigDialog(true)"
|
||||||
@ -123,7 +162,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="configForm.invalid || !changed" color="primary" mat-flat-button type="submit">
|
<button
|
||||||
|
[disabled]="configForm.invalid || !changed"
|
||||||
|
color="primary"
|
||||||
|
mat-flat-button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
{{ 'smtp-config-screen.actions.save' | translate }}
|
{{ 'smtp-config-screen.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@ -141,4 +185,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -65,32 +65,43 @@ export class SmtpConfigScreenComponent implements OnInit {
|
|||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this.viewReady = false;
|
this.viewReady = false;
|
||||||
await this._smtpConfigService.updateSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
await this._smtpConfigService
|
||||||
|
.updateSMTPConfiguration(this.configForm.getRawValue())
|
||||||
|
.toPromise();
|
||||||
this._initialValue = this.configForm.getRawValue();
|
this._initialValue = this.configForm.getRawValue();
|
||||||
this.viewReady = true;
|
this.viewReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
||||||
this._dialogService.openSMTPAuthConfigDialog(this.configForm.getRawValue(), (authConfig) => {
|
this._dialogService.openSMTPAuthConfigDialog(
|
||||||
if (authConfig) {
|
this.configForm.getRawValue(),
|
||||||
this.configForm.patchValue(authConfig);
|
(authConfig) => {
|
||||||
} else if (!skipDisableOnCancel) {
|
if (authConfig) {
|
||||||
this.configForm.patchValue({ auth: false }, { emitEvent: false });
|
this.configForm.patchValue(authConfig);
|
||||||
|
} else if (!skipDisableOnCancel) {
|
||||||
|
this.configForm.patchValue({ auth: false }, { emitEvent: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async testConnection() {
|
async testConnection() {
|
||||||
this.viewReady = false;
|
this.viewReady = false;
|
||||||
try {
|
try {
|
||||||
await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
await this._smtpConfigService
|
||||||
|
.testSMTPConfiguration(this.configForm.getRawValue())
|
||||||
|
.toPromise();
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant('smtp-config-screen.test.success'),
|
this._translateService.instant('smtp-config-screen.test.success'),
|
||||||
undefined,
|
undefined,
|
||||||
NotificationType.SUCCESS
|
NotificationType.SUCCESS
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._notificationService.showToastNotification(this._translateService.instant('smtp-config-screen.test.error'), undefined, NotificationType.ERROR);
|
this._notificationService.showToastNotification(
|
||||||
|
this._translateService.instant('smtp-config-screen.test.error'),
|
||||||
|
undefined,
|
||||||
|
NotificationType.ERROR
|
||||||
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.viewReady = true;
|
this.viewReady = true;
|
||||||
}
|
}
|
||||||
@ -98,7 +109,9 @@ export class SmtpConfigScreenComponent implements OnInit {
|
|||||||
|
|
||||||
private async _loadData() {
|
private async _loadData() {
|
||||||
try {
|
try {
|
||||||
this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
|
this._initialValue = await this._smtpConfigService
|
||||||
|
.getCurrentSMTPConfiguration()
|
||||||
|
.toPromise();
|
||||||
this.configForm.patchValue(this._initialValue, { emitEvent: false });
|
this.configForm.patchValue(this._initialValue, { emitEvent: false });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@ -8,7 +8,10 @@
|
|||||||
<div class="breadcrumb" translate="user-management"></div>
|
<div class="breadcrumb" translate="user-management"></div>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<redaction-search-input [form]="searchForm" [placeholder]="'user-listing.search'"></redaction-search-input>
|
<redaction-search-input
|
||||||
|
[form]="searchForm"
|
||||||
|
[placeholder]="'user-listing.search'"
|
||||||
|
></redaction-search-input>
|
||||||
<redaction-icon-button
|
<redaction-icon-button
|
||||||
(action)="openAddEditUserDialog($event)"
|
(action)="openAddEditUserDialog($event)"
|
||||||
*ngIf="permissionsService.isUserAdmin()"
|
*ngIf="permissionsService.isUserAdmin()"
|
||||||
@ -39,14 +42,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="all-caps-label">
|
<span class="all-caps-label">
|
||||||
{{ 'user-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
{{
|
||||||
|
'user-listing.table-header.title'
|
||||||
|
| translate: { length: displayedEntities.length }
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
|
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="bulkDelete()"
|
(action)="bulkDelete()"
|
||||||
[disabled]="!canDeleteSelected"
|
[disabled]="!canDeleteSelected"
|
||||||
[tooltip]="canDeleteSelected ? 'user-listing.bulk.delete' : 'user-listing.bulk.delete-disabled'"
|
[tooltip]="
|
||||||
|
canDeleteSelected
|
||||||
|
? 'user-listing.bulk.delete'
|
||||||
|
: 'user-listing.bulk.delete-disabled'
|
||||||
|
"
|
||||||
icon="red:trash"
|
icon="red:trash"
|
||||||
tooltipPosition="after"
|
tooltipPosition="after"
|
||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
@ -59,33 +69,56 @@
|
|||||||
<div class="table-header" redactionSyncWidth="table-item">
|
<div class="table-header" redactionSyncWidth="table-item">
|
||||||
<div class="select-oval-placeholder"></div>
|
<div class="select-oval-placeholder"></div>
|
||||||
|
|
||||||
<redaction-table-col-name label="user-listing.table-col-names.name"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
label="user-listing.table-col-names.name"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name label="user-listing.table-col-names.email"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
label="user-listing.table-col-names.email"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name class="flex-center" label="user-listing.table-col-names.active"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
class="flex-center"
|
||||||
|
label="user-listing.table-col-names.active"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<redaction-table-col-name label="user-listing.table-col-names.roles"></redaction-table-col-name>
|
<redaction-table-col-name
|
||||||
|
label="user-listing.table-col-names.roles"
|
||||||
|
></redaction-table-col-name>
|
||||||
|
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="scrollbar-placeholder"></div>
|
<div class="scrollbar-placeholder"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-empty-state *ngIf="!displayedEntities.length" screen="user-listing" type="no-match"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
*ngIf="!displayedEntities.length"
|
||||||
|
screen="user-listing"
|
||||||
|
type="no-match"
|
||||||
|
></redaction-empty-state>
|
||||||
|
|
||||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||||
<!-- Table lines -->
|
<!-- Table lines -->
|
||||||
<div *cdkVirtualFor="let user of displayedEntities" class="table-item">
|
<div *cdkVirtualFor="let user of displayedEntities" class="table-item">
|
||||||
<div (click)="toggleEntitySelected($event, user)" class="selection-column">
|
<div (click)="toggleEntitySelected($event, user)" class="selection-column">
|
||||||
<redaction-round-checkbox [active]="isEntitySelected(user)"></redaction-round-checkbox>
|
<redaction-round-checkbox
|
||||||
|
[active]="isEntitySelected(user)"
|
||||||
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<redaction-initials-avatar [showYou]="true" [user]="user" [withName]="true"></redaction-initials-avatar>
|
<redaction-initials-avatar
|
||||||
|
[showYou]="true"
|
||||||
|
[user]="user"
|
||||||
|
[withName]="true"
|
||||||
|
></redaction-initials-avatar>
|
||||||
</div>
|
</div>
|
||||||
<div class="small-label">{{ user.email || '-' }}</div>
|
<div class="small-label">{{ user.email || '-' }}</div>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<mat-slide-toggle (toggleChange)="toggleActive(user)" [checked]="userService.isActive(user)" color="primary"></mat-slide-toggle>
|
<mat-slide-toggle
|
||||||
|
(toggleChange)="toggleActive(user)"
|
||||||
|
[checked]="userService.isActive(user)"
|
||||||
|
color="primary"
|
||||||
|
></mat-slide-toggle>
|
||||||
</div>
|
</div>
|
||||||
<div class="small-label">{{ getDisplayRoles(user) }}</div>
|
<div class="small-label">{{ getDisplayRoles(user) }}</div>
|
||||||
<div class="actions-container">
|
<div class="actions-container">
|
||||||
@ -113,10 +146,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
|
<div [class.collapsed]="collapsedDetails" class="right-container" redactionHasScrollbar>
|
||||||
<redaction-users-stats (toggleCollapse)="toggleCollapsedDetails()" [chartData]="chartData"></redaction-users-stats>
|
<redaction-users-stats
|
||||||
|
(toggleCollapse)="toggleCollapsedDetails()"
|
||||||
|
[chartData]="chartData"
|
||||||
|
></redaction-users-stats>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -50,7 +50,9 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
|||||||
if (result.action === 'CREATE') {
|
if (result.action === 'CREATE') {
|
||||||
await this._userControllerService.createUser(result.user).toPromise();
|
await this._userControllerService.createUser(result.user).toPromise();
|
||||||
} else if (result.action === 'UPDATE') {
|
} else if (result.action === 'UPDATE') {
|
||||||
await this._userControllerService.updateProfile(result.user, user.userId).toPromise();
|
await this._userControllerService
|
||||||
|
.updateProfile(result.user, user.userId)
|
||||||
|
.toPromise();
|
||||||
}
|
}
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
}
|
}
|
||||||
@ -67,7 +69,10 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDisplayRoles(user: User) {
|
getDisplayRoles(user: User) {
|
||||||
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
|
return (
|
||||||
|
user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') ||
|
||||||
|
this._translateService.instant('roles.NO_ROLE')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleActive(user: User) {
|
async toggleActive(user: User) {
|
||||||
@ -101,32 +106,46 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
|||||||
this.chartData = this._translateChartService.translateRoles(
|
this.chartData = this._translateChartService.translateRoles(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => !this.userService.isActive(user)).length,
|
value: this.allEntities.filter((user) => !this.userService.isActive(user))
|
||||||
|
.length,
|
||||||
color: 'INACTIVE',
|
color: 'INACTIVE',
|
||||||
label: 'INACTIVE'
|
label: 'INACTIVE'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => user.roles.length === 1 && user.roles[0] === 'RED_USER').length,
|
value: this.allEntities.filter(
|
||||||
|
(user) => user.roles.length === 1 && user.roles[0] === 'RED_USER'
|
||||||
|
).length,
|
||||||
color: 'REGULAR',
|
color: 'REGULAR',
|
||||||
label: 'REGULAR'
|
label: 'REGULAR'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => this.userService.isManager(user) && !this.userService.isAdmin(user)).length,
|
value: this.allEntities.filter(
|
||||||
|
(user) =>
|
||||||
|
this.userService.isManager(user) && !this.userService.isAdmin(user)
|
||||||
|
).length,
|
||||||
color: 'MANAGER',
|
color: 'MANAGER',
|
||||||
label: 'RED_MANAGER'
|
label: 'RED_MANAGER'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => this.userService.isManager(user) && this.userService.isAdmin(user)).length,
|
value: this.allEntities.filter(
|
||||||
|
(user) => this.userService.isManager(user) && this.userService.isAdmin(user)
|
||||||
|
).length,
|
||||||
color: 'MANAGER_ADMIN',
|
color: 'MANAGER_ADMIN',
|
||||||
label: 'MANAGER_ADMIN'
|
label: 'MANAGER_ADMIN'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)).length,
|
value: this.allEntities.filter(
|
||||||
|
(user) =>
|
||||||
|
this.userService.isUserAdmin(user) && !this.userService.isAdmin(user)
|
||||||
|
).length,
|
||||||
color: 'USER_ADMIN',
|
color: 'USER_ADMIN',
|
||||||
label: 'RED_USER_ADMIN'
|
label: 'RED_USER_ADMIN'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.allEntities.filter((user) => this.userService.isAdmin(user) && !this.userService.isManager(user)).length,
|
value: this.allEntities.filter(
|
||||||
|
(user) =>
|
||||||
|
this.userService.isAdmin(user) && !this.userService.isManager(user)
|
||||||
|
).length,
|
||||||
color: 'ADMIN',
|
color: 'ADMIN',
|
||||||
label: 'RED_ADMIN'
|
label: 'RED_ADMIN'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,12 @@
|
|||||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||||
|
|
||||||
<div class="actions flex-1">
|
<div class="actions flex-1">
|
||||||
<redaction-circle-button [routerLink]="['../..']" icon="red:close" tooltip="common.close" tooltipPosition="below"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
icon="red:close"
|
||||||
|
tooltip="common.close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -22,7 +27,11 @@
|
|||||||
text="watermark-screen.action.save"
|
text="watermark-screen.action.save"
|
||||||
type="primary"
|
type="primary"
|
||||||
></redaction-icon-button>
|
></redaction-icon-button>
|
||||||
<div (click)="revert()" class="all-caps-label cancel" translate="watermark-screen.action.revert"></div>
|
<div
|
||||||
|
(click)="revert()"
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
translate="watermark-screen.action.revert"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -43,7 +52,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label class="all-caps-label mb-8" translate="watermark-screen.form.orientation"></label>
|
<label
|
||||||
|
class="all-caps-label mb-8"
|
||||||
|
translate="watermark-screen.form.orientation"
|
||||||
|
></label>
|
||||||
<div class="square-options">
|
<div class="square-options">
|
||||||
<div
|
<div
|
||||||
(click)="setValue('orientation', option)"
|
(click)="setValue('orientation', option)"
|
||||||
@ -58,17 +70,34 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label class="all-caps-label" translate="watermark-screen.form.font-size"></label>
|
<label
|
||||||
<mat-slider (change)="configChanged()" color="primary" formControlName="fontSize" max="50" min="5"></mat-slider>
|
class="all-caps-label"
|
||||||
|
translate="watermark-screen.form.font-size"
|
||||||
|
></label>
|
||||||
|
<mat-slider
|
||||||
|
(change)="configChanged()"
|
||||||
|
color="primary"
|
||||||
|
formControlName="fontSize"
|
||||||
|
max="50"
|
||||||
|
min="5"
|
||||||
|
></mat-slider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label class="all-caps-label" translate="watermark-screen.form.opacity"></label>
|
<label class="all-caps-label" translate="watermark-screen.form.opacity"></label>
|
||||||
<mat-slider (change)="configChanged()" color="primary" formControlName="opacity" min="1"></mat-slider>
|
<mat-slider
|
||||||
|
(change)="configChanged()"
|
||||||
|
color="primary"
|
||||||
|
formControlName="opacity"
|
||||||
|
min="1"
|
||||||
|
></mat-slider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group w-150">
|
<div class="red-input-group w-150">
|
||||||
<label class="all-caps-label mb-5" translate="watermark-screen.form.color"></label>
|
<label
|
||||||
|
class="all-caps-label mb-5"
|
||||||
|
translate="watermark-screen.form.color"
|
||||||
|
></label>
|
||||||
<input
|
<input
|
||||||
class="hex-color-input"
|
class="hex-color-input"
|
||||||
formControlName="hexColor"
|
formControlName="hexColor"
|
||||||
@ -88,14 +117,20 @@
|
|||||||
class="input-icon"
|
class="input-icon"
|
||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
*ngIf="!configForm.get('hexColor')?.value || configForm.get('hexColor').value?.length === 0"
|
*ngIf="
|
||||||
|
!configForm.get('hexColor')?.value ||
|
||||||
|
configForm.get('hexColor').value?.length === 0
|
||||||
|
"
|
||||||
svgIcon="red:color-picker"
|
svgIcon="red:color-picker"
|
||||||
></mat-icon>
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<label class="all-caps-label mb-8" translate="watermark-screen.form.font-type"></label>
|
<label
|
||||||
|
class="all-caps-label mb-8"
|
||||||
|
translate="watermark-screen.form.font-type"
|
||||||
|
></label>
|
||||||
<div class="square-options">
|
<div class="square-options">
|
||||||
<div
|
<div
|
||||||
(click)="setValue('fontType', option.value)"
|
(click)="setValue('fontType', option.value)"
|
||||||
@ -119,4 +154,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
|
<redaction-full-page-loading-indicator
|
||||||
|
[displayed]="!viewReady"
|
||||||
|
></redaction-full-page-loading-indicator>
|
||||||
|
|||||||
@ -78,20 +78,33 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const observable = watermark.text
|
const observable = watermark.text
|
||||||
? this._watermarkControllerService.saveWatermark(watermark, this.appStateService.activeRuleSetId)
|
? this._watermarkControllerService.saveWatermark(
|
||||||
: this._watermarkControllerService.deleteWatermark(this.appStateService.activeRuleSetId);
|
watermark,
|
||||||
|
this.appStateService.activeRuleSetId
|
||||||
|
)
|
||||||
|
: this._watermarkControllerService.deleteWatermark(
|
||||||
|
this.appStateService.activeRuleSetId
|
||||||
|
);
|
||||||
|
|
||||||
observable.subscribe(
|
observable.subscribe(
|
||||||
() => {
|
() => {
|
||||||
this._loadWatermark();
|
this._loadWatermark();
|
||||||
this._notificationService.showToastNotification(
|
this._notificationService.showToastNotification(
|
||||||
this._translateService.instant(watermark.text ? 'watermark-screen.action.change-success' : 'watermark-screen.action.delete-success'),
|
this._translateService.instant(
|
||||||
|
watermark.text
|
||||||
|
? 'watermark-screen.action.change-success'
|
||||||
|
: 'watermark-screen.action.delete-success'
|
||||||
|
),
|
||||||
null,
|
null,
|
||||||
NotificationType.SUCCESS
|
NotificationType.SUCCESS
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this._notificationService.showToastNotification(this._translateService.instant('watermark-screen.action.error'), null, NotificationType.ERROR);
|
this._notificationService.showToastNotification(
|
||||||
|
this._translateService.instant('watermark-screen.action.error'),
|
||||||
|
null,
|
||||||
|
NotificationType.ERROR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -111,18 +124,20 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _loadWatermark() {
|
private _loadWatermark() {
|
||||||
this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe(
|
this._watermarkControllerService
|
||||||
(watermark) => {
|
.getWatermark(this.appStateService.activeRuleSetId)
|
||||||
this._watermark = watermark;
|
.subscribe(
|
||||||
this.configForm.setValue({ ...this._watermark });
|
(watermark) => {
|
||||||
this._loadViewer();
|
this._watermark = watermark;
|
||||||
},
|
this.configForm.setValue({ ...this._watermark });
|
||||||
() => {
|
this._loadViewer();
|
||||||
this._watermark = DEFAULT_WATERMARK;
|
},
|
||||||
this.configForm.setValue({ ...this._watermark });
|
() => {
|
||||||
this._loadViewer();
|
this._watermark = DEFAULT_WATERMARK;
|
||||||
}
|
this.configForm.setValue({ ...this._watermark });
|
||||||
);
|
this._loadViewer();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _loadViewer() {
|
private _loadViewer() {
|
||||||
@ -177,14 +192,17 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
const text = this.configForm.get('text').value || '';
|
const text = this.configForm.get('text').value || '';
|
||||||
const fontSize = this.configForm.get('fontSize').value;
|
const fontSize = this.configForm.get('fontSize').value;
|
||||||
const fontType = this.configForm.get('fontType').value;
|
const fontType = this.configForm.get('fontType').value;
|
||||||
const orientation: WatermarkModel.WatermarkOrientationEnum = this.configForm.get('orientation').value;
|
const orientation: WatermarkModel.WatermarkOrientationEnum =
|
||||||
|
this.configForm.get('orientation').value;
|
||||||
const opacity = this.configForm.get('opacity').value;
|
const opacity = this.configForm.get('opacity').value;
|
||||||
const color = this.configForm.get('hexColor').value;
|
const color = this.configForm.get('hexColor').value;
|
||||||
|
|
||||||
const rgbColor = hexToRgb(color);
|
const rgbColor = hexToRgb(color);
|
||||||
|
|
||||||
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
|
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.setFontColor(
|
||||||
|
await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
|
||||||
|
);
|
||||||
await stamper.setOpacity(opacity / 100);
|
await stamper.setOpacity(opacity / 100);
|
||||||
|
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
@ -200,7 +218,10 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
await stamper.setRotation(-45);
|
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.setFont(font);
|
||||||
await stamper.setTextAlignment(0);
|
await stamper.setTextAlignment(0);
|
||||||
await stamper.stampText(document, text, pageSet);
|
await stamper.stampText(document, text, pageSet);
|
||||||
@ -216,11 +237,26 @@ export class WatermarkScreenComponent implements OnInit {
|
|||||||
private _initForm() {
|
private _initForm() {
|
||||||
this.configForm = this._formBuilder.group({
|
this.configForm = this._formBuilder.group({
|
||||||
text: [{ value: null, disabled: !this.permissionsService.isAdmin() }],
|
text: [{ value: null, disabled: !this.permissionsService.isAdmin() }],
|
||||||
hexColor: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required],
|
hexColor: [
|
||||||
opacity: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required],
|
{ value: null, disabled: !this.permissionsService.isAdmin() },
|
||||||
fontSize: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required],
|
Validators.required
|
||||||
fontType: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required],
|
],
|
||||||
orientation: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required]
|
opacity: [
|
||||||
|
{ value: null, disabled: !this.permissionsService.isAdmin() },
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
fontSize: [
|
||||||
|
{ value: null, disabled: !this.permissionsService.isAdmin() },
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
fontType: [
|
||||||
|
{ value: null, disabled: !this.permissionsService.isAdmin() },
|
||||||
|
Validators.required
|
||||||
|
],
|
||||||
|
orientation: [
|
||||||
|
{ value: null, disabled: !this.permissionsService.isAdmin() },
|
||||||
|
Validators.required
|
||||||
|
]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -53,19 +53,30 @@ export class AdminDialogService {
|
|||||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService
|
private readonly _manualRedactionControllerService: ManualRedactionControllerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
openDeleteDictionaryDialog(
|
||||||
|
$event: MouseEvent,
|
||||||
|
dictionary: TypeValue,
|
||||||
|
ruleSetId: string,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<ConfirmationDialogComponent> {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||||
ref.afterClosed().subscribe(async (result) => {
|
ref.afterClosed().subscribe(async (result) => {
|
||||||
if (result) {
|
if (result) {
|
||||||
await this._dictionaryControllerService.deleteType(dictionary.type, ruleSetId).toPromise();
|
await this._dictionaryControllerService
|
||||||
|
.deleteType(dictionary.type, ruleSetId)
|
||||||
|
.toPromise();
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openDeleteRuleSetDialog($event: MouseEvent, ruleSet: RuleSetModel, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
openDeleteRuleSetDialog(
|
||||||
|
$event: MouseEvent,
|
||||||
|
ruleSet: RuleSetModel,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<ConfirmationDialogComponent> {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||||
ref.afterClosed().subscribe(async (result) => {
|
ref.afterClosed().subscribe(async (result) => {
|
||||||
@ -77,7 +88,11 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openAddEditDictionaryDialog(dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<AddEditDictionaryDialogComponent> {
|
openAddEditDictionaryDialog(
|
||||||
|
dictionary: TypeValue,
|
||||||
|
ruleSetId: string,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<AddEditDictionaryDialogComponent> {
|
||||||
const ref = this._dialog.open(AddEditDictionaryDialogComponent, {
|
const ref = this._dialog.open(AddEditDictionaryDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
data: { dictionary, ruleSetId },
|
data: { dictionary, ruleSetId },
|
||||||
@ -93,7 +108,12 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
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, {
|
const ref = this._dialog.open(EditColorDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
data: { colors, colorKey, ruleSetId },
|
data: { colors, colorKey, ruleSetId },
|
||||||
@ -109,7 +129,10 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openAddEditRuleSetDialog(ruleSet: RuleSetModel, cb?: Function): MatDialogRef<AddEditRuleSetDialogComponent> {
|
openAddEditRuleSetDialog(
|
||||||
|
ruleSet: RuleSetModel,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<AddEditRuleSetDialogComponent> {
|
||||||
const ref = this._dialog.open(AddEditRuleSetDialogComponent, {
|
const ref = this._dialog.open(AddEditRuleSetDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
width: '900px',
|
width: '900px',
|
||||||
@ -146,7 +169,11 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openAddEditFileAttributeDialog(fileAttribute: FileAttributeConfig, ruleSetId: string, cb?: Function): MatDialogRef<AddEditFileAttributeDialogComponent> {
|
openAddEditFileAttributeDialog(
|
||||||
|
fileAttribute: FileAttributeConfig,
|
||||||
|
ruleSetId: string,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<AddEditFileAttributeDialogComponent> {
|
||||||
const ref = this._dialog.open(AddEditFileAttributeDialogComponent, {
|
const ref = this._dialog.open(AddEditFileAttributeDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
data: { fileAttribute, ruleSetId },
|
data: { fileAttribute, ruleSetId },
|
||||||
@ -182,7 +209,10 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openSMTPAuthConfigDialog(smtpConfig: SMTPConfigurationModel, cb?: Function): MatDialogRef<SmtpAuthDialogComponent> {
|
openSMTPAuthConfigDialog(
|
||||||
|
smtpConfig: SMTPConfigurationModel,
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<SmtpAuthDialogComponent> {
|
||||||
const ref = this._dialog.open(SmtpAuthDialogComponent, {
|
const ref = this._dialog.open(SmtpAuthDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
data: smtpConfig,
|
data: smtpConfig,
|
||||||
@ -214,7 +244,10 @@ export class AdminDialogService {
|
|||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
openConfirmDeleteUsersDialog(users: User[], cb?: Function): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
openConfirmDeleteUsersDialog(
|
||||||
|
users: User[],
|
||||||
|
cb?: Function
|
||||||
|
): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
||||||
const ref = this._dialog.open(ConfirmDeleteUsersDialogComponent, {
|
const ref = this._dialog.open(ConfirmDeleteUsersDialogComponent, {
|
||||||
...dialogConfig,
|
...dialogConfig,
|
||||||
data: users,
|
data: users,
|
||||||
|
|||||||
@ -3,7 +3,9 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
import { version } from '../../../../../../package.json';
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
import packageInfo from '../../../../../../package.json';
|
||||||
import { CacheApiService, wipeCaches } from '@redaction/red-cache';
|
import { CacheApiService, wipeCaches } from '@redaction/red-cache';
|
||||||
|
|
||||||
export enum AppConfigKey {
|
export enum AppConfigKey {
|
||||||
@ -34,23 +36,37 @@ export enum AppConfigKey {
|
|||||||
export class AppConfigService {
|
export class AppConfigService {
|
||||||
private _config: { [key in AppConfigKey]?: any } = {};
|
private _config: { [key in AppConfigKey]?: any } = {};
|
||||||
|
|
||||||
constructor(private readonly _httpClient: HttpClient, private readonly _cacheApiService: CacheApiService, private readonly _titleService: Title) {}
|
constructor(
|
||||||
|
private readonly _httpClient: HttpClient,
|
||||||
|
private readonly _cacheApiService: CacheApiService,
|
||||||
|
private readonly _titleService: Title
|
||||||
|
) {}
|
||||||
|
|
||||||
loadAppConfig(): Observable<any> {
|
loadAppConfig(): Observable<any> {
|
||||||
this._cacheApiService.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION).then(async (lastVersion) => {
|
this._cacheApiService
|
||||||
console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', version);
|
.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION)
|
||||||
if (lastVersion !== version) {
|
.then(async (lastVersion) => {
|
||||||
console.log('[REDACTION] Version-missmatch - wiping caches!');
|
console.log(
|
||||||
await wipeCaches();
|
'[REDACTION] Last app version: ',
|
||||||
}
|
lastVersion,
|
||||||
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, version);
|
' current version ',
|
||||||
});
|
this.version
|
||||||
|
);
|
||||||
|
if (lastVersion !== this.version) {
|
||||||
|
console.log('[REDACTION] Version-missmatch - wiping caches!');
|
||||||
|
await wipeCaches();
|
||||||
|
}
|
||||||
|
await this._cacheApiService.cacheValue(
|
||||||
|
AppConfigKey.FRONTEND_APP_VERSION,
|
||||||
|
this.version
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return this._httpClient.get<any>('/assets/config/config.json').pipe(
|
return this._httpClient.get<any>('/assets/config/config.json').pipe(
|
||||||
tap((config) => {
|
tap((config) => {
|
||||||
console.log('[REDACTION] Started with config: ', config);
|
console.log('[REDACTION] Started with config: ', config);
|
||||||
this._config = config;
|
this._config = config;
|
||||||
this._config[AppConfigKey.FRONTEND_APP_VERSION] = version;
|
this._config[AppConfigKey.FRONTEND_APP_VERSION] = this.version;
|
||||||
this._titleService.setTitle(this.getConfig(AppConfigKey.APP_NAME, 'DDA-R'));
|
this._titleService.setTitle(this.getConfig(AppConfigKey.APP_NAME, 'DDA-R'));
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -59,4 +75,8 @@ export class AppConfigService {
|
|||||||
getConfig(key: AppConfigKey | string, defaultValue?: any) {
|
getConfig(key: AppConfigKey | string, defaultValue?: any) {
|
||||||
return this._config[key] ? this._config[key] : defaultValue;
|
return this._config[key] ? this._config[key] : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get version(): string {
|
||||||
|
return packageInfo.version;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,11 @@ import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloa
|
|||||||
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
||||||
import { BASE_HREF } from '../../tokens';
|
import { BASE_HREF } from '../../tokens';
|
||||||
|
|
||||||
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) {
|
export function keycloakInitializer(
|
||||||
|
keycloak: KeycloakService,
|
||||||
|
appConfigService: AppConfigService,
|
||||||
|
baseUrl
|
||||||
|
) {
|
||||||
return () =>
|
return () =>
|
||||||
appConfigService
|
appConfigService
|
||||||
.loadAppConfig()
|
.loadAppConfig()
|
||||||
@ -26,12 +30,15 @@ export function keycloakInitializer(keycloak: KeycloakService, appConfigService:
|
|||||||
initOptions: {
|
initOptions: {
|
||||||
checkLoginIframe: false,
|
checkLoginIframe: false,
|
||||||
onLoad: 'check-sso',
|
onLoad: 'check-sso',
|
||||||
silentCheckSsoRedirectUri: window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
|
silentCheckSsoRedirectUri:
|
||||||
|
window.location.origin + baseUrl + '/assets/oauth/silent-refresh.html',
|
||||||
flow: 'standard'
|
flow: 'standard'
|
||||||
},
|
},
|
||||||
enableBearerInterceptor: true
|
enableBearerInterceptor: true
|
||||||
};
|
};
|
||||||
return keycloak.init(options).then(() => configureAutomaticRedirectToLoginScreen(keycloak));
|
return keycloak
|
||||||
|
.init(options)
|
||||||
|
.then(() => configureAutomaticRedirectToLoginScreen(keycloak));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,11 @@ import { Observable } from 'rxjs';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class RedRoleGuard implements CanActivate {
|
export class RedRoleGuard implements CanActivate {
|
||||||
constructor(protected readonly _router: Router, private readonly _appLoadStateService: AppLoadStateService, private readonly _userService: UserService) {}
|
constructor(
|
||||||
|
protected readonly _router: Router,
|
||||||
|
private readonly _appLoadStateService: AppLoadStateService,
|
||||||
|
private readonly _userService: UserService
|
||||||
|
) {}
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||||
return new Observable((obs) => {
|
return new Observable((obs) => {
|
||||||
@ -20,7 +24,11 @@ export class RedRoleGuard implements CanActivate {
|
|||||||
} else {
|
} else {
|
||||||
// we have at least 1 RED Role -> if it's not user he must be admin
|
// we have at least 1 RED Role -> if it's not user he must be admin
|
||||||
|
|
||||||
if (this._userService.user.isUserAdmin && !this._userService.user.isAdmin && !state.url.startsWith('/main/admin/users')) {
|
if (
|
||||||
|
this._userService.user.isUserAdmin &&
|
||||||
|
!this._userService.user.isAdmin &&
|
||||||
|
!state.url.startsWith('/main/admin/users')
|
||||||
|
) {
|
||||||
this._router.navigate(['/main/admin/users']);
|
this._router.navigate(['/main/admin/users']);
|
||||||
obs.next(false);
|
obs.next(false);
|
||||||
obs.complete();
|
obs.complete();
|
||||||
|
|||||||
@ -9,7 +9,10 @@ import { DomSanitizer } from '@angular/platform-browser';
|
|||||||
exports: [MatIconModule]
|
exports: [MatIconModule]
|
||||||
})
|
})
|
||||||
export class IconsModule {
|
export class IconsModule {
|
||||||
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
|
constructor(
|
||||||
|
private readonly _iconRegistry: MatIconRegistry,
|
||||||
|
private readonly _sanitizer: DomSanitizer
|
||||||
|
) {
|
||||||
const icons = [
|
const icons = [
|
||||||
'add',
|
'add',
|
||||||
'analyse',
|
'analyse',
|
||||||
@ -84,7 +87,11 @@ export class IconsModule {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (const icon of icons) {
|
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`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,13 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, [annotation], annotationsChanged)"
|
(action)="
|
||||||
|
annotationActionsService.convertRecommendationToAnnotation(
|
||||||
|
$event,
|
||||||
|
[annotation],
|
||||||
|
annotationsChanged
|
||||||
|
)
|
||||||
|
"
|
||||||
*ngIf="annotationPermissions.canAcceptRecommendation"
|
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||||
icon="red:check"
|
icon="red:check"
|
||||||
tooltip="annotation-actions.accept-recommendation.label"
|
tooltip="annotation-actions.accept-recommendation.label"
|
||||||
@ -20,8 +26,13 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)"
|
(action)="
|
||||||
*ngIf="annotationPermissions.canMarkTextOnlyAsFalsePositive && !annotationPermissions.canPerformMultipleRemoveActions"
|
annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)
|
||||||
|
"
|
||||||
|
*ngIf="
|
||||||
|
annotationPermissions.canMarkTextOnlyAsFalsePositive &&
|
||||||
|
!annotationPermissions.canPerformMultipleRemoveActions
|
||||||
|
"
|
||||||
icon="red:thumb-down"
|
icon="red:thumb-down"
|
||||||
tooltip="annotation-actions.remove-annotation.false-positive"
|
tooltip="annotation-actions.remove-annotation.false-positive"
|
||||||
tooltipPosition="before"
|
tooltipPosition="before"
|
||||||
@ -30,7 +41,9 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.acceptSuggestion($event, [annotation], annotationsChanged)"
|
(action)="
|
||||||
|
annotationActionsService.acceptSuggestion($event, [annotation], annotationsChanged)
|
||||||
|
"
|
||||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||||
icon="red:check"
|
icon="red:check"
|
||||||
tooltip="annotation-actions.accept-suggestion.label"
|
tooltip="annotation-actions.accept-suggestion.label"
|
||||||
@ -40,7 +53,9 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.undoDirectAction($event, [annotation], annotationsChanged)"
|
(action)="
|
||||||
|
annotationActionsService.undoDirectAction($event, [annotation], annotationsChanged)
|
||||||
|
"
|
||||||
*ngIf="annotationPermissions.canUndo"
|
*ngIf="annotationPermissions.canUndo"
|
||||||
icon="red:undo"
|
icon="red:undo"
|
||||||
tooltip="annotation-actions.undo"
|
tooltip="annotation-actions.undo"
|
||||||
@ -70,7 +85,9 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="annotationActionsService.rejectSuggestion($event, [annotation], annotationsChanged)"
|
(action)="
|
||||||
|
annotationActionsService.rejectSuggestion($event, [annotation], annotationsChanged)
|
||||||
|
"
|
||||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||||
icon="red:close"
|
icon="red:close"
|
||||||
tooltip="annotation-actions.reject-suggestion"
|
tooltip="annotation-actions.reject-suggestion"
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="suggestRemoveAnnotations($event, false)"
|
(action)="suggestRemoveAnnotations($event, false)"
|
||||||
*ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere && permissions.canNotPerformMultipleRemoveActions"
|
*ngIf="
|
||||||
|
permissions.canRemoveOrSuggestToRemoveOnlyHere &&
|
||||||
|
permissions.canNotPerformMultipleRemoveActions
|
||||||
|
"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[type]="btnType"
|
[type]="btnType"
|
||||||
icon="red:trash"
|
icon="red:trash"
|
||||||
@ -21,16 +24,36 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||||
<div (click)="suggestRemoveAnnotations($event, true)" *ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary" mat-menu-item>
|
<div
|
||||||
<redaction-annotation-icon [color]="dictionaryColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
(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 [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||||
</div>
|
</div>
|
||||||
<div (click)="suggestRemoveAnnotations($event, false)" *ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere" mat-menu-item>
|
<div
|
||||||
<redaction-annotation-icon [color]="suggestionColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
(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 translate="annotation-actions.remove-annotation.only-here"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div (click)="markAsFalsePositive($event)" *ngIf="permissions.canMarkAsFalsePositive" mat-menu-item>
|
<div
|
||||||
|
(click)="markAsFalsePositive($event)"
|
||||||
|
*ngIf="permissions.canMarkAsFalsePositive"
|
||||||
|
mat-menu-item
|
||||||
|
>
|
||||||
<mat-icon class="false-positive-icon" svgIcon="red:thumb-down"></mat-icon>
|
<mat-icon class="false-positive-icon" svgIcon="red:thumb-down"></mat-icon>
|
||||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -65,20 +65,44 @@ export class AnnotationRemoveActionsComponent {
|
|||||||
|
|
||||||
suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._annotationActionsService.suggestRemoveAnnotation($event, this.annotations, removeFromDict, this.annotationsChanged);
|
this._annotationActionsService.suggestRemoveAnnotation(
|
||||||
|
$event,
|
||||||
|
this.annotations,
|
||||||
|
removeFromDict,
|
||||||
|
this.annotationsChanged
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
markAsFalsePositive($event) {
|
markAsFalsePositive($event) {
|
||||||
this._annotationActionsService.markAsFalsePositive($event, this.annotations, this.annotationsChanged);
|
this._annotationActionsService.markAsFalsePositive(
|
||||||
|
$event,
|
||||||
|
this.annotations,
|
||||||
|
this.annotationsChanged
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setPermissions() {
|
private _setPermissions() {
|
||||||
this.permissions = {
|
this.permissions = {
|
||||||
canRemoveOrSuggestToRemoveOnlyHere: this._annotationsPermissions(['canRemoveOrSuggestToRemoveOnlyHere'], true),
|
canRemoveOrSuggestToRemoveOnlyHere: this._annotationsPermissions(
|
||||||
canPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], true),
|
['canRemoveOrSuggestToRemoveOnlyHere'],
|
||||||
canNotPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], false),
|
true
|
||||||
canRemoveOrSuggestToRemoveFromDictionary: this._annotationsPermissions(['canRemoveOrSuggestToRemoveFromDictionary'], true),
|
),
|
||||||
canMarkAsFalsePositive: this._annotationsPermissions(['canMarkAsFalsePositive', 'canMarkTextOnlyAsFalsePositive'], true)
|
canPerformMultipleRemoveActions: this._annotationsPermissions(
|
||||||
|
['canPerformMultipleRemoveActions'],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
canNotPerformMultipleRemoveActions: this._annotationsPermissions(
|
||||||
|
['canPerformMultipleRemoveActions'],
|
||||||
|
false
|
||||||
|
),
|
||||||
|
canRemoveOrSuggestToRemoveFromDictionary: this._annotationsPermissions(
|
||||||
|
['canRemoveOrSuggestToRemoveFromDictionary'],
|
||||||
|
true
|
||||||
|
),
|
||||||
|
canMarkAsFalsePositive: this._annotationsPermissions(
|
||||||
|
['canMarkAsFalsePositive', 'canMarkTextOnlyAsFalsePositive'],
|
||||||
|
true
|
||||||
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +113,10 @@ export class AnnotationRemoveActionsComponent {
|
|||||||
this._permissionsService.currentUser,
|
this._permissionsService.currentUser,
|
||||||
annotation
|
annotation
|
||||||
);
|
);
|
||||||
const hasAtLeastOnePermission = keys.reduce((acc, key) => acc || annotationPermissions[key] === expectedValue, false);
|
const hasAtLeastOnePermission = keys.reduce(
|
||||||
|
(acc, key) => acc || annotationPermissions[key] === expectedValue,
|
||||||
|
false
|
||||||
|
);
|
||||||
return prevValue && hasAtLeastOnePermission;
|
return prevValue && hasAtLeastOnePermission;
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,21 @@
|
|||||||
type="dark-bg"
|
type="dark-bg"
|
||||||
></redaction-circle-button>
|
></redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button (action)="assign()" *ngIf="canAssign" icon="red:assign" [tooltip]="assignTooltip" type="dark-bg"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
(action)="assign()"
|
||||||
|
*ngIf="canAssign"
|
||||||
|
icon="red:assign"
|
||||||
|
[tooltip]="assignTooltip"
|
||||||
|
type="dark-bg"
|
||||||
|
></redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button (action)="assignToMe()" *ngIf="canAssignToSelf" icon="red:assign-me" tooltip="project-overview.assign-me" type="dark-bg">
|
<redaction-circle-button
|
||||||
|
(action)="assignToMe()"
|
||||||
|
*ngIf="canAssignToSelf"
|
||||||
|
icon="red:assign-me"
|
||||||
|
tooltip="project-overview.assign-me"
|
||||||
|
type="dark-bg"
|
||||||
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
@ -21,10 +33,19 @@
|
|||||||
>
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button (action)="setToUnderReview()" *ngIf="canSetToUnderReview" icon="red:undo" tooltip="project-overview.under-review" type="dark-bg">
|
<redaction-circle-button
|
||||||
|
(action)="setToUnderReview()"
|
||||||
|
*ngIf="canSetToUnderReview"
|
||||||
|
icon="red:undo"
|
||||||
|
tooltip="project-overview.under-review"
|
||||||
|
type="dark-bg"
|
||||||
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<redaction-file-download-btn [file]="selectedFiles" [project]="project"></redaction-file-download-btn>
|
<redaction-file-download-btn
|
||||||
|
[file]="selectedFiles"
|
||||||
|
[project]="project"
|
||||||
|
></redaction-file-download-btn>
|
||||||
|
|
||||||
<!-- Approved-->
|
<!-- Approved-->
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
@ -38,10 +59,22 @@
|
|||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
|
|
||||||
<!-- Back to approval -->
|
<!-- Back to approval -->
|
||||||
<redaction-circle-button (action)="setToUnderApproval()" *ngIf="canUndoApproval" icon="red:undo" tooltip="project-overview.under-approval" type="dark-bg">
|
<redaction-circle-button
|
||||||
|
(action)="setToUnderApproval()"
|
||||||
|
*ngIf="canUndoApproval"
|
||||||
|
icon="red:undo"
|
||||||
|
tooltip="project-overview.under-approval"
|
||||||
|
type="dark-bg"
|
||||||
|
>
|
||||||
</redaction-circle-button>
|
</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)="ocr()"
|
||||||
|
*ngIf="canOcr"
|
||||||
|
icon="red:ocr"
|
||||||
|
tooltip="project-overview.ocr-file"
|
||||||
|
type="dark-bg"
|
||||||
|
></redaction-circle-button>
|
||||||
|
|
||||||
<redaction-circle-button
|
<redaction-circle-button
|
||||||
(action)="reanalyse()"
|
(action)="reanalyse()"
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { UserService } from '@services/user.service';
|
import { UserService } from '@services/user.service';
|
||||||
import { FileManagementControllerService, ReanalysisControllerService } from '@redaction/red-ui-http';
|
import {
|
||||||
|
FileManagementControllerService,
|
||||||
|
ReanalysisControllerService
|
||||||
|
} from '@redaction/red-ui-http';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||||
import { FileActionService } from '../../services/file-action.service';
|
import { FileActionService } from '../../services/file-action.service';
|
||||||
@ -39,11 +42,19 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get selectedFiles(): FileStatusWrapper[] {
|
get selectedFiles(): FileStatusWrapper[] {
|
||||||
return this.selectedFileIds.map((fileId) => this._appStateService.getFileById(this._appStateService.activeProject.project.projectId, fileId));
|
return this.selectedFileIds.map((fileId) =>
|
||||||
|
this._appStateService.getFileById(
|
||||||
|
this._appStateService.activeProject.project.projectId,
|
||||||
|
fileId
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get areAllFilesSelected() {
|
get areAllFilesSelected() {
|
||||||
return this._appStateService.activeProject.files.length !== 0 && this.selectedFileIds.length === this._appStateService.activeProject.files.length;
|
return (
|
||||||
|
this._appStateService.activeProject.files.length !== 0 &&
|
||||||
|
this.selectedFileIds.length === this._appStateService.activeProject.files.length
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get areSomeFilesSelected() {
|
get areSomeFilesSelected() {
|
||||||
@ -53,8 +64,14 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
get allSelectedFilesCanBeAssignedIntoSameState() {
|
get allSelectedFilesCanBeAssignedIntoSameState() {
|
||||||
if (this.areSomeFilesSelected) {
|
if (this.areSomeFilesSelected) {
|
||||||
const selectedFiles = this.selectedFiles;
|
const selectedFiles = this.selectedFiles;
|
||||||
const allFilesAreUnderReviewOrUnassigned = selectedFiles.reduce((acc, file) => acc && (file.isUnderReview || file.isUnassigned), true);
|
const allFilesAreUnderReviewOrUnassigned = selectedFiles.reduce(
|
||||||
const allFilesAreUnderApproval = selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
(acc, file) => acc && (file.isUnderReview || file.isUnassigned),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
const allFilesAreUnderApproval = selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && file.isUnderApproval,
|
||||||
|
true
|
||||||
|
);
|
||||||
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
return allFilesAreUnderReviewOrUnassigned || allFilesAreUnderApproval;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -63,27 +80,42 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
get canAssignToSelf() {
|
get canAssignToSelf() {
|
||||||
return (
|
return (
|
||||||
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
||||||
this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignToSelf(file), true)
|
this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canAssignToSelf(file),
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAssign() {
|
get canAssign() {
|
||||||
return (
|
return (
|
||||||
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
this.allSelectedFilesCanBeAssignedIntoSameState &&
|
||||||
this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignUser(file), true)
|
this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canAssignUser(file),
|
||||||
|
true
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canDelete() {
|
get canDelete() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canDeleteFile(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canDeleteFile(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canReanalyse() {
|
get canReanalyse() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canReanalyseFile(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canReanalyseFile(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canOcr() {
|
get canOcr() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canOcrFile(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canOcrFile(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get fileStatuses() {
|
get fileStatuses() {
|
||||||
@ -92,47 +124,74 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
|
|
||||||
// Under review
|
// Under review
|
||||||
get canSetToUnderReview() {
|
get canSetToUnderReview() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderReview(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canSetUnderReview(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Under approval
|
// Under approval
|
||||||
get canSetToUnderApproval() {
|
get canSetToUnderApproval() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canSetUnderApproval(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canSetUnderApproval(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Approve
|
// Approve
|
||||||
get isReadyForApproval() {
|
get isReadyForApproval() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.isReadyForApproval(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.isReadyForApproval(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canApprove() {
|
get canApprove() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canApprove(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canApprove(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undo approval
|
// Undo approval
|
||||||
get canUndoApproval() {
|
get canUndoApproval() {
|
||||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canUndoApproval(file), true);
|
return this.selectedFiles.reduce(
|
||||||
|
(acc, file) => acc && this._permissionsService.canUndoApproval(file),
|
||||||
|
true
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get assignTooltip() {
|
get assignTooltip() {
|
||||||
const allFilesAreUnderApproval = this.selectedFiles.reduce((acc, file) => acc && file.isUnderApproval, true);
|
const allFilesAreUnderApproval = this.selectedFiles.reduce(
|
||||||
return allFilesAreUnderApproval ? 'project-overview.assign-approver' : 'project-overview.assign-reviewer';
|
(acc, file) => acc && file.isUnderApproval,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
return allFilesAreUnderApproval
|
||||||
|
? 'project-overview.assign-approver'
|
||||||
|
: 'project-overview.assign-reviewer';
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this._dialogService.openDeleteFilesDialog(null, this._appStateService.activeProjectId, this.selectedFileIds, () => {
|
this._dialogService.openDeleteFilesDialog(
|
||||||
this.reload.emit();
|
null,
|
||||||
this.loading = false;
|
this._appStateService.activeProjectId,
|
||||||
this.selectedFileIds.splice(0, this.selectedFileIds.length);
|
this.selectedFileIds,
|
||||||
});
|
() => {
|
||||||
|
this.reload.emit();
|
||||||
|
this.loading = false;
|
||||||
|
this.selectedFileIds.splice(0, this.selectedFileIds.length);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setToUnderApproval() {
|
setToUnderApproval() {
|
||||||
// If more than 1 approver - show dialog and ask who to assign
|
// If more than 1 approver - show dialog and ask who to assign
|
||||||
if (this._appStateService.activeProject.approverIds.length > 1) {
|
if (this._appStateService.activeProject.approverIds.length > 1) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const files = this.selectedFileIds.map((fileId) => this._appStateService.getFileById(this._appStateService.activeProjectId, fileId));
|
const files = this.selectedFileIds.map((fileId) =>
|
||||||
|
this._appStateService.getFileById(this._appStateService.activeProjectId, fileId)
|
||||||
|
);
|
||||||
|
|
||||||
this._dialogService.openAssignFileToUserDialog(
|
this._dialogService.openAssignFileToUserDialog(
|
||||||
files,
|
files,
|
||||||
@ -144,13 +203,25 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._performBulkAction(this._fileActionService.setFileUnderApproval(this.selectedFiles, this._appStateService.activeProject.approverIds[0]));
|
this._performBulkAction(
|
||||||
|
this._fileActionService.setFileUnderApproval(
|
||||||
|
this.selectedFiles,
|
||||||
|
this._appStateService.activeProject.approverIds[0]
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async reanalyse() {
|
async reanalyse() {
|
||||||
const fileIds = this.selectedFiles.filter((file) => this._permissionsService.fileRequiresReanalysis(file)).map((file) => file.fileId);
|
const fileIds = this.selectedFiles
|
||||||
this._performBulkAction(this._reanalysisControllerService.reanalyzeFilesForProject(fileIds, this._appStateService.activeProject.projectId));
|
.filter((file) => this._permissionsService.fileRequiresReanalysis(file))
|
||||||
|
.map((file) => file.fileId);
|
||||||
|
this._performBulkAction(
|
||||||
|
this._reanalysisControllerService.reanalyzeFilesForProject(
|
||||||
|
fileIds,
|
||||||
|
this._appStateService.activeProject.projectId
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ocr() {
|
ocr() {
|
||||||
@ -175,7 +246,9 @@ export class ProjectOverviewBulkActionsComponent {
|
|||||||
|
|
||||||
assign() {
|
assign() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const files = this.selectedFileIds.map((fileId) => this._appStateService.getFileById(this._appStateService.activeProjectId, fileId));
|
const files = this.selectedFileIds.map((fileId) =>
|
||||||
|
this._appStateService.getFileById(this._appStateService.activeProjectId, fileId)
|
||||||
|
);
|
||||||
|
|
||||||
const mode = files[0].isUnderApproval ? 'approver' : 'reviewer';
|
const mode = files[0].isUnderApproval ? 'approver' : 'reviewer';
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<ng-container *ngIf="expanded">
|
<ng-container *ngIf="expanded">
|
||||||
<div *ngFor="let comment of annotation.comments; let idx = index" class="comment">
|
<div *ngFor="let comment of annotation.comments; let idx = index" class="comment">
|
||||||
<div [class.comment-owner]="isCommentOwner(comment)" [class.red]="isCommentOwner(comment)" class="comment-icon">
|
<div
|
||||||
<mat-icon [svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"></mat-icon>
|
[class.comment-owner]="isCommentOwner(comment)"
|
||||||
|
[class.red]="isCommentOwner(comment)"
|
||||||
|
class="comment-icon"
|
||||||
|
>
|
||||||
|
<mat-icon
|
||||||
|
[svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"
|
||||||
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
<div (click)="deleteComment(comment)" [class.comment-owner]="isCommentOwner(comment)" class="trash-icon red">
|
<div
|
||||||
|
(click)="deleteComment(comment)"
|
||||||
|
[class.comment-owner]="isCommentOwner(comment)"
|
||||||
|
class="trash-icon red"
|
||||||
|
>
|
||||||
<mat-icon svgIcon="red:trash"></mat-icon>
|
<mat-icon svgIcon="red:trash"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,9 +30,14 @@
|
|||||||
{{
|
{{
|
||||||
expanded
|
expanded
|
||||||
? translateService.instant('comments.hide-comments')
|
? translateService.instant('comments.hide-comments')
|
||||||
: translateService.instant(annotation.comments.length === 1 ? 'comments.comment' : 'comments.comments', {
|
: translateService.instant(
|
||||||
count: annotation.comments.length
|
annotation.comments.length === 1
|
||||||
})
|
? 'comments.comment'
|
||||||
|
: 'comments.comments',
|
||||||
|
{
|
||||||
|
count: annotation.comments.length
|
||||||
|
}
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -32,14 +47,33 @@
|
|||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form (submit)="addComment()" *ngIf="addingComment && permissionsService.canAddComment()" [formGroup]="commentForm">
|
<form
|
||||||
|
(submit)="addComment()"
|
||||||
|
*ngIf="addingComment && permissionsService.canAddComment()"
|
||||||
|
[formGroup]="commentForm"
|
||||||
|
>
|
||||||
<div class="red-input-group">
|
<div class="red-input-group">
|
||||||
<input [placeholder]="translateService.instant('comments.add-comment')" class="w-full" formControlName="comment" name="comment" type="text" />
|
<input
|
||||||
|
[placeholder]="translateService.instant('comments.add-comment')"
|
||||||
|
class="w-full"
|
||||||
|
formControlName="comment"
|
||||||
|
name="comment"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div *ngIf="addingComment" class="comment-actions-container">
|
<div *ngIf="addingComment" class="comment-actions-container">
|
||||||
<redaction-circle-button (action)="addComment()" [disabled]="!commentForm.value.comment" icon="red:check" type="primary"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
<div (click)="toggleAddingComment($event)" class="all-caps-label cancel" translate="comments.cancel"></div>
|
(action)="addComment()"
|
||||||
|
[disabled]="!commentForm.value.comment"
|
||||||
|
icon="red:check"
|
||||||
|
type="primary"
|
||||||
|
></redaction-circle-button>
|
||||||
|
<div
|
||||||
|
(click)="toggleAddingComment($event)"
|
||||||
|
class="all-caps-label cancel"
|
||||||
|
translate="comments.cancel"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -58,25 +58,29 @@ export class CommentsComponent {
|
|||||||
addComment(): void {
|
addComment(): void {
|
||||||
const value = this.commentForm.value.comment;
|
const value = this.commentForm.value.comment;
|
||||||
if (value) {
|
if (value) {
|
||||||
this._manualAnnotationService.addComment(value, this.annotation.id).subscribe((commentResponse) => {
|
this._manualAnnotationService
|
||||||
this.annotation.comments.push({
|
.addComment(value, this.annotation.id)
|
||||||
text: value,
|
.subscribe((commentResponse) => {
|
||||||
id: commentResponse.commentId,
|
this.annotation.comments.push({
|
||||||
user: this._userService.userId
|
text: value,
|
||||||
|
id: commentResponse.commentId,
|
||||||
|
user: this._userService.userId
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
this.commentForm.reset();
|
this.commentForm.reset();
|
||||||
this.toggleAddingComment();
|
this.toggleAddingComment();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteComment(comment: Comment): void {
|
deleteComment(comment: Comment): void {
|
||||||
this._manualAnnotationService.deleteComment(comment.id, this.annotation.id).subscribe(() => {
|
this._manualAnnotationService
|
||||||
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
.deleteComment(comment.id, this.annotation.id)
|
||||||
if (!this.annotation.comments.length) {
|
.subscribe(() => {
|
||||||
this.expanded = false;
|
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
||||||
}
|
if (!this.annotation.comments.length) {
|
||||||
});
|
this.expanded = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isCommentOwner(comment: Comment): boolean {
|
isCommentOwner(comment: Comment): boolean {
|
||||||
|
|||||||
@ -26,19 +26,31 @@
|
|||||||
<div class="section small-label stats-subtitle">
|
<div class="section small-label stats-subtitle">
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:folder"></mat-icon>
|
<mat-icon svgIcon="red:folder"></mat-icon>
|
||||||
<span>{{ 'file-preview.tabs.document-info.details.project' | translate: { projectName: project.name } }}</span>
|
<span>{{
|
||||||
|
'file-preview.tabs.document-info.details.project'
|
||||||
|
| translate: { projectName: project.name }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:document"></mat-icon>
|
<mat-icon svgIcon="red:document"></mat-icon>
|
||||||
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate: { pages: file.numberOfPages } }}</span>
|
<span>{{
|
||||||
|
'file-preview.tabs.document-info.details.pages'
|
||||||
|
| translate: { pages: file.numberOfPages }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
<span>{{ 'file-preview.tabs.document-info.details.created-on' | translate: { date: file.added | date: 'mediumDate' } }}</span>
|
<span>{{
|
||||||
|
'file-preview.tabs.document-info.details.created-on'
|
||||||
|
| translate: { date: file.added | date: 'mediumDate' }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="project.project.dueDate">
|
<div *ngIf="project.project.dueDate">
|
||||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||||
<span>{{ 'file-preview.tabs.document-info.details.due' | translate: { date: project.project.dueDate | date: 'mediumDate' } }}</span>
|
<span>{{
|
||||||
|
'file-preview.tabs.document-info.details.due'
|
||||||
|
| translate: { date: project.project.dueDate | date: 'mediumDate' }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:template"></mat-icon>
|
<mat-icon svgIcon="red:template"></mat-icon>
|
||||||
|
|||||||
@ -14,7 +14,10 @@ export class DocumentInfoComponent {
|
|||||||
|
|
||||||
fileAttributesConfig: FileAttributesConfig;
|
fileAttributesConfig: FileAttributesConfig;
|
||||||
|
|
||||||
constructor(private readonly _appStateService: AppStateService, private readonly _dialogService: ProjectsDialogService) {
|
constructor(
|
||||||
|
private readonly _appStateService: AppStateService,
|
||||||
|
private readonly _dialogService: ProjectsDialogService
|
||||||
|
) {
|
||||||
this.fileAttributesConfig = this._appStateService.activeFileAttributesConfig;
|
this.fileAttributesConfig = this._appStateService.activeFileAttributesConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,11 @@
|
|||||||
*ngIf="permissionsService.isReadyForApproval(fileStatus)"
|
*ngIf="permissionsService.isReadyForApproval(fileStatus)"
|
||||||
[disabled]="!permissionsService.canApprove(fileStatus)"
|
[disabled]="!permissionsService.canApprove(fileStatus)"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="permissionsService.canApprove(fileStatus) ? 'project-overview.approve' : 'project-overview.approve-disabled'"
|
[tooltip]="
|
||||||
|
permissionsService.canApprove(fileStatus)
|
||||||
|
? 'project-overview.approve'
|
||||||
|
: 'project-overview.approve-disabled'
|
||||||
|
"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
icon="red:approved"
|
icon="red:approved"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -38,7 +38,9 @@ export class FileActionsComponent implements OnInit {
|
|||||||
return 'file-preview.toggle-analysis.only-managers';
|
return 'file-preview.toggle-analysis.only-managers';
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.fileStatus?.isExcluded ? 'file-preview.toggle-analysis.enable' : 'file-preview.toggle-analysis.disable';
|
return this.fileStatus?.isExcluded
|
||||||
|
? 'file-preview.toggle-analysis.enable'
|
||||||
|
: 'file-preview.toggle-analysis.disable';
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAssignToSelf() {
|
get canAssignToSelf() {
|
||||||
@ -86,7 +88,9 @@ export class FileActionsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get assignTooltip() {
|
get assignTooltip() {
|
||||||
return this.fileStatus.isUnderApproval ? 'project-overview.assign-approver' : 'project-overview.assign-reviewer';
|
return this.fileStatus.isUnderApproval
|
||||||
|
? 'project-overview.assign-approver'
|
||||||
|
: 'project-overview.assign-reviewer';
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@ -108,9 +112,14 @@ export class FileActionsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openDeleteFileDialog($event: MouseEvent) {
|
openDeleteFileDialog($event: MouseEvent) {
|
||||||
this._dialogService.openDeleteFilesDialog($event, this.fileStatus.projectId, [this.fileStatus.fileId], () => {
|
this._dialogService.openDeleteFilesDialog(
|
||||||
this.actionPerformed.emit('delete');
|
$event,
|
||||||
});
|
this.fileStatus.projectId,
|
||||||
|
[this.fileStatus.fileId],
|
||||||
|
() => {
|
||||||
|
this.actionPerformed.emit('delete');
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assign($event: MouseEvent) {
|
assign($event: MouseEvent) {
|
||||||
@ -137,7 +146,11 @@ export class FileActionsComponent implements OnInit {
|
|||||||
setFileUnderApproval($event: MouseEvent) {
|
setFileUnderApproval($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
if (this.appStateService.activeProject.approverIds.length > 1) {
|
if (this.appStateService.activeProject.approverIds.length > 1) {
|
||||||
this._fileActionService.assignProjectApprover(this.fileStatus, () => this.actionPerformed.emit('assign-reviewer'), true);
|
this._fileActionService.assignProjectApprover(
|
||||||
|
this.fileStatus,
|
||||||
|
() => this.actionPerformed.emit('assign-reviewer'),
|
||||||
|
true
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this._fileActionService.setFileUnderApproval(this.fileStatus).subscribe(() => {
|
this._fileActionService.setFileUnderApproval(this.fileStatus).subscribe(() => {
|
||||||
this.reloadProjects('set-under-approval');
|
this.reloadProjects('set-under-approval');
|
||||||
@ -161,7 +174,11 @@ export class FileActionsComponent implements OnInit {
|
|||||||
|
|
||||||
setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
this._fileActionService.assignProjectReviewer(this.fileStatus, () => this.actionPerformed.emit('assign-reviewer'), ignoreDialogChanges);
|
this._fileActionService.assignProjectReviewer(
|
||||||
|
this.fileStatus,
|
||||||
|
() => this.actionPerformed.emit('assign-reviewer'),
|
||||||
|
ignoreDialogChanges
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
reloadProjects(action: string) {
|
reloadProjects(action: string) {
|
||||||
@ -174,6 +191,8 @@ export class FileActionsComponent implements OnInit {
|
|||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise();
|
await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise();
|
||||||
await this.appStateService.getFiles();
|
await this.appStateService.getFiles();
|
||||||
this.actionPerformed.emit(this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis');
|
this.actionPerformed.emit(
|
||||||
|
this.fileStatus?.isExcluded ? 'enable-analysis' : 'disable-analysis'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,11 @@
|
|||||||
tooltipPosition="above"
|
tooltipPosition="above"
|
||||||
></redaction-annotation-remove-actions>
|
></redaction-annotation-remove-actions>
|
||||||
</div>
|
</div>
|
||||||
<redaction-circle-button (action)="multiSelectActive = false" icon="red:close" type="primary"></redaction-circle-button>
|
<redaction-circle-button
|
||||||
|
(action)="multiSelectActive = false"
|
||||||
|
icon="red:close"
|
||||||
|
type="primary"
|
||||||
|
></redaction-circle-button>
|
||||||
</div>
|
</div>
|
||||||
<div [class.multi-select-active]="multiSelectActive" class="annotations-wrapper">
|
<div [class.multi-select-active]="multiSelectActive" class="annotations-wrapper">
|
||||||
<div
|
<div
|
||||||
@ -78,13 +82,24 @@
|
|||||||
<div style="overflow: hidden; width: 100%">
|
<div style="overflow: hidden; width: 100%">
|
||||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
><span translate="page"></span> {{ activeViewerPage }} -
|
||||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
{{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||||
|
<span
|
||||||
|
[translate]="
|
||||||
|
displayedAnnotations[activeViewerPage]?.annotations?.length === 1
|
||||||
|
? 'annotation'
|
||||||
|
: 'annotations'
|
||||||
|
"
|
||||||
|
></span
|
||||||
></span>
|
></span>
|
||||||
|
|
||||||
<div *ngIf="multiSelectActive">
|
<div *ngIf="multiSelectActive">
|
||||||
<div (click)="selectAllOnActivePage()" class="all-caps-label primary pointer">All</div>
|
<div (click)="selectAllOnActivePage()" class="all-caps-label primary pointer">
|
||||||
<div (click)="deselectAllOnActivePage()" class="all-caps-label primary pointer">None</div>
|
All
|
||||||
|
</div>
|
||||||
|
<div (click)="deselectAllOnActivePage()" class="all-caps-label primary pointer">
|
||||||
|
None
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -98,7 +113,12 @@
|
|||||||
tabindex="1"
|
tabindex="1"
|
||||||
>
|
>
|
||||||
<ng-container *ngIf="!displayedAnnotations[activeViewerPage]">
|
<ng-container *ngIf="!displayedAnnotations[activeViewerPage]">
|
||||||
<redaction-empty-state [horizontalPadding]="24" [verticalPadding]="40" icon="red:document" screen="file-preview"></redaction-empty-state>
|
<redaction-empty-state
|
||||||
|
[horizontalPadding]="24"
|
||||||
|
[verticalPadding]="40"
|
||||||
|
icon="red:document"
|
||||||
|
screen="file-preview"
|
||||||
|
></redaction-empty-state>
|
||||||
<div class="no-annotations-buttons-container mt-32">
|
<div class="no-annotations-buttons-container mt-32">
|
||||||
<redaction-icon-button
|
<redaction-icon-button
|
||||||
(action)="jumpToPreviousWithAnnotations()"
|
(action)="jumpToPreviousWithAnnotations()"
|
||||||
@ -109,7 +129,9 @@
|
|||||||
></redaction-icon-button>
|
></redaction-icon-button>
|
||||||
<redaction-icon-button
|
<redaction-icon-button
|
||||||
(action)="jumpToNextWithAnnotations()"
|
(action)="jumpToNextWithAnnotations()"
|
||||||
[disabled]="activeViewerPage >= displayedPages[displayedPages.length - 1]"
|
[disabled]="
|
||||||
|
activeViewerPage >= displayedPages[displayedPages.length - 1]
|
||||||
|
"
|
||||||
class="mt-8"
|
class="mt-8"
|
||||||
icon="red:nav-next"
|
icon="red:nav-next"
|
||||||
text="file-preview.tabs.annotations.jump-to-next"
|
text="file-preview.tabs.annotations.jump-to-next"
|
||||||
@ -129,21 +151,32 @@
|
|||||||
>
|
>
|
||||||
<div class="active-bar-marker"></div>
|
<div class="active-bar-marker"></div>
|
||||||
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
||||||
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
|
<redaction-hidden-action
|
||||||
|
(action)="logAnnotation(annotation)"
|
||||||
|
[requiredClicks]="2"
|
||||||
|
>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
<redaction-type-annotation-icon
|
||||||
|
[annotation]="annotation"
|
||||||
|
></redaction-type-annotation-icon>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
<div
|
||||||
|
*ngIf="
|
||||||
|
annotation.dictionary &&
|
||||||
|
annotation.dictionary !== 'manual'
|
||||||
|
"
|
||||||
|
>
|
||||||
<strong
|
<strong
|
||||||
><span>{{ annotation.descriptor | translate }}</span
|
><span>{{ annotation.descriptor | translate }}</span
|
||||||
>: </strong
|
>: </strong
|
||||||
>{{ annotation.dictionary | humanize: false }}
|
>{{ annotation.dictionary | humanize: false }}
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="annotation.content && !annotation.isHint">
|
<div *ngIf="annotation.content && !annotation.isHint">
|
||||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
<strong><span translate="content"></span>: </strong
|
||||||
|
>{{ annotation.content }}
|
||||||
</div>
|
</div>
|
||||||
{{ annotation.id }}
|
{{ annotation.id }}
|
||||||
</div>
|
</div>
|
||||||
@ -155,7 +188,9 @@
|
|||||||
</ng-container>
|
</ng-container>
|
||||||
<div class="active-icon-marker-container">
|
<div class="active-icon-marker-container">
|
||||||
<redaction-round-checkbox
|
<redaction-round-checkbox
|
||||||
*ngIf="multiSelectActive && annotationIsSelected(annotation)"
|
*ngIf="
|
||||||
|
multiSelectActive && annotationIsSelected(annotation)
|
||||||
|
"
|
||||||
[active]="true"
|
[active]="true"
|
||||||
></redaction-round-checkbox>
|
></redaction-round-checkbox>
|
||||||
</div>
|
</div>
|
||||||
@ -170,16 +205,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ng-template #annotationFilterTemplate let-filter="filter">
|
<ng-template #annotationFilterTemplate let-filter="filter">
|
||||||
<redaction-type-filter *ngIf="_(filter).topLevelFilter" [filter]="filter"></redaction-type-filter>
|
<redaction-type-filter
|
||||||
|
*ngIf="_(filter).topLevelFilter"
|
||||||
|
[filter]="filter"
|
||||||
|
></redaction-type-filter>
|
||||||
<ng-container *ngIf="!_(filter).topLevelFilter">
|
<ng-container *ngIf="!_(filter).topLevelFilter">
|
||||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.key"></redaction-dictionary-annotation-icon>
|
<redaction-dictionary-annotation-icon
|
||||||
|
[dictionaryKey]="filter.key"
|
||||||
|
></redaction-dictionary-annotation-icon>
|
||||||
{{ filter.key | humanize: false }}
|
{{ filter.key | humanize: false }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
||||||
<ng-container *ngIf="filter.key === 'skipped'">
|
<ng-container *ngIf="filter.key === 'skipped'">
|
||||||
<redaction-circle-button (action)="toggleSkipped.emit($event)" [icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'" type="dark-bg">
|
<redaction-circle-button
|
||||||
|
(action)="toggleSkipped.emit($event)"
|
||||||
|
[icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'"
|
||||||
|
type="dark-bg"
|
||||||
|
>
|
||||||
</redaction-circle-button>
|
</redaction-circle-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|||||||
@ -1,4 +1,14 @@
|
|||||||
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
TemplateRef,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
import { FilterModel } from '@shared/components/filter/model/filter.model';
|
import { FilterModel } from '@shared/components/filter/model/filter.model';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||||
@ -27,7 +37,9 @@ export class FileWorkloadComponent {
|
|||||||
@Input() hideSkipped: boolean;
|
@Input() hideSkipped: boolean;
|
||||||
@Input() annotationActionsTemplate: TemplateRef<any>;
|
@Input() annotationActionsTemplate: TemplateRef<any>;
|
||||||
@Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
|
@Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
|
||||||
@Output() selectAnnotations = new EventEmitter<AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }>();
|
@Output() selectAnnotations = new EventEmitter<
|
||||||
|
AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
|
||||||
|
>();
|
||||||
@Output() deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
@Output() deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||||
@Output() selectPage = new EventEmitter<number>();
|
@Output() selectPage = new EventEmitter<number>();
|
||||||
@Output() toggleSkipped = new EventEmitter<any>();
|
@Output() toggleSkipped = new EventEmitter<any>();
|
||||||
@ -37,7 +49,10 @@ export class FileWorkloadComponent {
|
|||||||
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||||
|
|
||||||
constructor(private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _annotationProcessingService: AnnotationProcessingService) {}
|
constructor(
|
||||||
|
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private readonly _annotationProcessingService: AnnotationProcessingService
|
||||||
|
) {}
|
||||||
|
|
||||||
private _annotations: AnnotationWrapper[];
|
private _annotations: AnnotationWrapper[];
|
||||||
|
|
||||||
@ -66,7 +81,10 @@ export class FileWorkloadComponent {
|
|||||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
|
private static _scrollToFirstElement(
|
||||||
|
elements: HTMLElement[],
|
||||||
|
mode: 'always' | 'if-needed' = 'if-needed'
|
||||||
|
) {
|
||||||
if (elements.length > 0) {
|
if (elements.length > 0) {
|
||||||
scrollIntoView(elements[0], {
|
scrollIntoView(elements[0], {
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
@ -86,7 +104,9 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pageHasSelection(page: number) {
|
pageHasSelection(page: number) {
|
||||||
return this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page);
|
return (
|
||||||
|
this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAllOnActivePage() {
|
selectAllOnActivePage() {
|
||||||
@ -101,7 +121,11 @@ export class FileWorkloadComponent {
|
|||||||
|
|
||||||
@debounce(0)
|
@debounce(0)
|
||||||
filtersChanged(filters: { primary: FilterModel[]; secondary?: FilterModel[] }) {
|
filtersChanged(filters: { primary: FilterModel[]; secondary?: FilterModel[] }) {
|
||||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this._annotations, filters.primary, filters.secondary);
|
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(
|
||||||
|
this._annotations,
|
||||||
|
filters.primary,
|
||||||
|
filters.secondary
|
||||||
|
);
|
||||||
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
||||||
this._changeDetectorRef.markForCheck();
|
this._changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
@ -114,13 +138,20 @@ export class FileWorkloadComponent {
|
|||||||
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||||
this.multiSelectActive = true;
|
this.multiSelectActive = true;
|
||||||
}
|
}
|
||||||
this.selectAnnotations.emit({ annotations: [annotation], multiSelect: this.multiSelectActive });
|
this.selectAnnotations.emit({
|
||||||
|
annotations: [annotation],
|
||||||
|
multiSelect: this.multiSelectActive
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:keyup', ['$event'])
|
@HostListener('window:keyup', ['$event'])
|
||||||
handleKeyEvent($event: KeyboardEvent) {
|
handleKeyEvent($event: KeyboardEvent) {
|
||||||
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN || ($event.target as any).localName === 'input') {
|
if (
|
||||||
|
!ALL_HOTKEY_ARRAY.includes($event.key) ||
|
||||||
|
this.dialogRef?.getState() === MatDialogState.OPEN ||
|
||||||
|
($event.target as any).localName === 'input'
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +162,8 @@ export class FileWorkloadComponent {
|
|||||||
|
|
||||||
if ($event.key === 'ArrowRight') {
|
if ($event.key === 'ArrowRight') {
|
||||||
this.pagesPanelActive = false;
|
this.pagesPanelActive = false;
|
||||||
// if we activated annotationsPanel - select first annotation from this page in case there is no
|
// if we activated annotationsPanel -
|
||||||
|
// select first annotation from this page in case there is no
|
||||||
// selected annotation on this page and not in multi select mode
|
// selected annotation on this page and not in multi select mode
|
||||||
if (!this.pagesPanelActive && !this.multiSelectActive) {
|
if (!this.pagesPanelActive && !this.multiSelectActive) {
|
||||||
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
||||||
@ -140,7 +172,8 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.pagesPanelActive) {
|
if (!this.pagesPanelActive) {
|
||||||
// Disable annotation navigation in multi select mode => TODO: maybe implement selection on enter?
|
// Disable annotation navigation in multi select mode
|
||||||
|
// => TODO: maybe implement selection on enter?
|
||||||
if (!this.multiSelectActive) {
|
if (!this.multiSelectActive) {
|
||||||
this._navigateAnnotations($event);
|
this._navigateAnnotations($event);
|
||||||
}
|
}
|
||||||
@ -159,7 +192,9 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed') {
|
scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed') {
|
||||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[anotation-page-header="${page}"]`);
|
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(
|
||||||
|
`div[anotation-page-header="${page}"]`
|
||||||
|
);
|
||||||
FileWorkloadComponent._scrollToFirstElement(elements, mode);
|
FileWorkloadComponent._scrollToFirstElement(elements, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,13 +203,18 @@ export class FileWorkloadComponent {
|
|||||||
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
|
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[annotation-id="${this._firstSelectedAnnotation?.id}"].active`);
|
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(
|
||||||
|
`div[annotation-id="${this._firstSelectedAnnotation?.id}"].active`
|
||||||
|
);
|
||||||
FileWorkloadComponent._scrollToFirstElement(elements);
|
FileWorkloadComponent._scrollToFirstElement(elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollQuickNavigation() {
|
scrollQuickNavigation() {
|
||||||
let quickNavPageIndex = this.displayedPages.findIndex((p) => p >= this.activeViewerPage);
|
let quickNavPageIndex = this.displayedPages.findIndex((p) => p >= this.activeViewerPage);
|
||||||
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== this.activeViewerPage) {
|
if (
|
||||||
|
quickNavPageIndex === -1 ||
|
||||||
|
this.displayedPages[quickNavPageIndex] !== this.activeViewerPage
|
||||||
|
) {
|
||||||
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
|
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
|
||||||
}
|
}
|
||||||
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
|
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
|
||||||
@ -194,7 +234,10 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preventKeyDefault($event: KeyboardEvent) {
|
preventKeyDefault($event: KeyboardEvent) {
|
||||||
if (COMMAND_KEY_ARRAY.includes($event.key) && !(($event.target as any).localName === 'input')) {
|
if (
|
||||||
|
COMMAND_KEY_ARRAY.includes($event.key) &&
|
||||||
|
!(($event.target as any).localName === 'input')
|
||||||
|
) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,39 +256,53 @@ export class FileWorkloadComponent {
|
|||||||
|
|
||||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||||
if (
|
if (
|
||||||
(!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
|
(!this._firstSelectedAnnotation ||
|
||||||
|
this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
|
||||||
this.displayedPages.indexOf(this.activeViewerPage) >= 0
|
this.displayedPages.indexOf(this.activeViewerPage) >= 0
|
||||||
) {
|
) {
|
||||||
this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
|
this.selectAnnotations.emit([
|
||||||
|
this.displayedAnnotations[this.activeViewerPage].annotations[0]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _navigateAnnotations($event: KeyboardEvent) {
|
private _navigateAnnotations($event: KeyboardEvent) {
|
||||||
if (!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) {
|
if (
|
||||||
|
!this._firstSelectedAnnotation ||
|
||||||
|
this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber
|
||||||
|
) {
|
||||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||||
if (pageIdx !== -1) {
|
if (pageIdx !== -1) {
|
||||||
// Displayed page has annotations
|
// Displayed page has annotations
|
||||||
this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
|
this.selectAnnotations.emit([
|
||||||
|
this.displayedAnnotations[this.activeViewerPage].annotations[0]
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
// Displayed page doesn't have annotations
|
// Displayed page doesn't have annotations
|
||||||
if ($event.key === 'ArrowDown') {
|
if ($event.key === 'ArrowDown') {
|
||||||
const nextPage = this._nextPageWithAnnotations();
|
const nextPage = this._nextPageWithAnnotations();
|
||||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||||
this.selectAnnotations.emit([this.displayedAnnotations[nextPage].annotations[0]]);
|
this.selectAnnotations.emit([
|
||||||
|
this.displayedAnnotations[nextPage].annotations[0]
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
const prevPage = this._prevPageWithAnnotations();
|
const prevPage = this._prevPageWithAnnotations();
|
||||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
this.selectAnnotations.emit([
|
||||||
|
prevPageAnnotations[prevPageAnnotations.length - 1]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const page = this._firstSelectedAnnotation.pageNumber;
|
const page = this._firstSelectedAnnotation.pageNumber;
|
||||||
const pageIdx = this.displayedPages.indexOf(page);
|
const pageIdx = this.displayedPages.indexOf(page);
|
||||||
const annotationsOnPage = this.displayedAnnotations[page].annotations;
|
const annotationsOnPage = this.displayedAnnotations[page].annotations;
|
||||||
const idx = annotationsOnPage.findIndex((a) => a.id === this._firstSelectedAnnotation.id);
|
const idx = annotationsOnPage.findIndex(
|
||||||
|
(a) => a.id === this._firstSelectedAnnotation.id
|
||||||
|
);
|
||||||
|
|
||||||
if ($event.key === 'ArrowDown') {
|
if ($event.key === 'ArrowDown') {
|
||||||
if (idx + 1 !== annotationsOnPage.length) {
|
if (idx + 1 !== annotationsOnPage.length) {
|
||||||
@ -253,7 +310,8 @@ export class FileWorkloadComponent {
|
|||||||
this.selectAnnotations.emit([annotationsOnPage[idx + 1]]);
|
this.selectAnnotations.emit([annotationsOnPage[idx + 1]]);
|
||||||
} else if (pageIdx + 1 < this.displayedPages.length) {
|
} else if (pageIdx + 1 < this.displayedPages.length) {
|
||||||
// If not last page
|
// If not last page
|
||||||
const nextPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
const nextPageAnnotations =
|
||||||
|
this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
||||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||||
this.selectAnnotations.emit([nextPageAnnotations[0]]);
|
this.selectAnnotations.emit([nextPageAnnotations[0]]);
|
||||||
@ -264,10 +322,13 @@ export class FileWorkloadComponent {
|
|||||||
this.selectAnnotations.emit([annotationsOnPage[idx - 1]]);
|
this.selectAnnotations.emit([annotationsOnPage[idx - 1]]);
|
||||||
} else if (pageIdx) {
|
} else if (pageIdx) {
|
||||||
// If not first page
|
// If not first page
|
||||||
const prevPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx - 1]].annotations;
|
const prevPageAnnotations =
|
||||||
|
this.displayedAnnotations[this.displayedPages[pageIdx - 1]].annotations;
|
||||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
this.selectAnnotations.emit([
|
||||||
|
prevPageAnnotations[prevPageAnnotations.length - 1]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +390,9 @@ export class FileWorkloadComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _scrollQuickNavigationToPage(page: number) {
|
private _scrollQuickNavigationToPage(page: number) {
|
||||||
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
|
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(
|
||||||
|
`#quick-nav-page-${page}`
|
||||||
|
);
|
||||||
FileWorkloadComponent._scrollToFirstElement(elements);
|
FileWorkloadComponent._scrollToFirstElement(elements);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,39 @@
|
|||||||
<div class="needs-work">
|
<div class="needs-work">
|
||||||
<redaction-annotation-icon *ngIf="reanalysisRequired()" [color]="analysisColor" label="A" type="square"></redaction-annotation-icon>
|
<redaction-annotation-icon
|
||||||
<redaction-annotation-icon *ngIf="hasUpdates" [color]="updatedColor" label="U" type="square"></redaction-annotation-icon>
|
*ngIf="reanalysisRequired()"
|
||||||
<redaction-annotation-icon *ngIf="needsWorkInput.hasRedactions" [color]="redactionColor" label="R" type="square"></redaction-annotation-icon>
|
[color]="analysisColor"
|
||||||
<redaction-annotation-icon *ngIf="hasImages" [color]="imageColor" label="I" type="square"></redaction-annotation-icon>
|
label="A"
|
||||||
<redaction-annotation-icon *ngIf="needsWorkInput.hintsOnly" [color]="hintColor" label="H" type="circle"></redaction-annotation-icon>
|
type="square"
|
||||||
<redaction-annotation-icon *ngIf="needsWorkInput.hasRequests" [color]="suggestionColor" label="S" type="rhombus"></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
*ngIf="hasUpdates"
|
||||||
|
[color]="updatedColor"
|
||||||
|
label="U"
|
||||||
|
type="square"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
*ngIf="needsWorkInput.hasRedactions"
|
||||||
|
[color]="redactionColor"
|
||||||
|
label="R"
|
||||||
|
type="square"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
*ngIf="hasImages"
|
||||||
|
[color]="imageColor"
|
||||||
|
label="I"
|
||||||
|
type="square"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
*ngIf="needsWorkInput.hintsOnly"
|
||||||
|
[color]="hintColor"
|
||||||
|
label="H"
|
||||||
|
type="circle"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
*ngIf="needsWorkInput.hasRequests"
|
||||||
|
[color]="suggestionColor"
|
||||||
|
label="S"
|
||||||
|
type="rhombus"
|
||||||
|
></redaction-annotation-icon>
|
||||||
<mat-icon *ngIf="hasAnnotationComments" svgIcon="red:comment"></mat-icon>
|
<mat-icon *ngIf="hasAnnotationComments" svgIcon="red:comment"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import '/apps/red-ui/src/assets/styles/red-variables';
|
@import 'apps/red-ui/src/assets/styles/red-variables';
|
||||||
|
|
||||||
.needs-work {
|
.needs-work {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -12,7 +12,10 @@ import { ProjectWrapper } from '@state/model/project.wrapper';
|
|||||||
export class NeedsWorkBadgeComponent {
|
export class NeedsWorkBadgeComponent {
|
||||||
@Input() needsWorkInput: FileStatusWrapper | ProjectWrapper;
|
@Input() needsWorkInput: FileStatusWrapper | ProjectWrapper;
|
||||||
|
|
||||||
constructor(private readonly _appStateService: AppStateService, private readonly _permissionsService: PermissionsService) {}
|
constructor(
|
||||||
|
private readonly _appStateService: AppStateService,
|
||||||
|
private readonly _permissionsService: PermissionsService
|
||||||
|
) {}
|
||||||
|
|
||||||
get suggestionColor() {
|
get suggestionColor() {
|
||||||
return this._getDictionaryColor('suggestion');
|
return this._getDictionaryColor('suggestion');
|
||||||
@ -47,7 +50,10 @@ export class NeedsWorkBadgeComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasAnnotationComments(): boolean {
|
get hasAnnotationComments(): boolean {
|
||||||
return this.needsWorkInput instanceof FileStatusWrapper && (<any>this.needsWorkInput).hasAnnotationComments;
|
return (
|
||||||
|
this.needsWorkInput instanceof FileStatusWrapper &&
|
||||||
|
(<any>this.needsWorkInput).hasAnnotationComments
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
reanalysisRequired() {
|
reanalysisRequired() {
|
||||||
|
|||||||
@ -1,4 +1,13 @@
|
|||||||
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
SimpleChanges
|
||||||
|
} from '@angular/core';
|
||||||
import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http';
|
import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http';
|
||||||
import { AppStateService } from '@state/app-state.service';
|
import { AppStateService } from '@state/app-state.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
@ -95,15 +104,25 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
|||||||
|
|
||||||
private _markPageRead() {
|
private _markPageRead() {
|
||||||
this._viewedPagesControllerService
|
this._viewedPagesControllerService
|
||||||
.addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId)
|
.addPage(
|
||||||
|
{ page: this.number },
|
||||||
|
this._appStateService.activeProjectId,
|
||||||
|
this._appStateService.activeFileId
|
||||||
|
)
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.viewedPages?.pages?.push(this.number);
|
this.viewedPages?.pages?.push(this.number);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _markPageUnread() {
|
private _markPageUnread() {
|
||||||
this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => {
|
this._viewedPagesControllerService
|
||||||
this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
|
.removePage(
|
||||||
});
|
this._appStateService.activeProjectId,
|
||||||
|
this._appStateService.activeFileId,
|
||||||
|
this.number
|
||||||
|
)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,17 @@
|
|||||||
import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, NgZone, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
|
import {
|
||||||
|
AfterViewInit,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
Inject,
|
||||||
|
Input,
|
||||||
|
NgZone,
|
||||||
|
OnChanges,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
|
ViewChild
|
||||||
|
} from '@angular/core';
|
||||||
import { ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
|
import { ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
|
||||||
import WebViewer, { Annotations, Tools, WebViewerInstance } from '@pdftron/webviewer';
|
import WebViewer, { Annotations, Tools, WebViewerInstance } from '@pdftron/webviewer';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
@ -72,7 +85,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
this.instance.annotManager.deselectAllAnnotations();
|
this.instance.annotManager.deselectAllAnnotations();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
|
selectAnnotations(
|
||||||
|
$event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
|
||||||
|
) {
|
||||||
let annotations: AnnotationWrapper[];
|
let annotations: AnnotationWrapper[];
|
||||||
let multiSelect: boolean;
|
let multiSelect: boolean;
|
||||||
if ($event instanceof Array) {
|
if ($event instanceof Array) {
|
||||||
@ -87,14 +102,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
this.deselectAllAnnotations();
|
this.deselectAllAnnotations();
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotationsFromViewer = annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id));
|
const annotationsFromViewer = annotations.map((ann) =>
|
||||||
|
this.instance.annotManager.getAnnotationById(ann.id)
|
||||||
|
);
|
||||||
this.instance.annotManager.selectAnnotations(annotationsFromViewer);
|
this.instance.annotManager.selectAnnotations(annotationsFromViewer);
|
||||||
this.navigateToPage(annotations[0].pageNumber);
|
this.navigateToPage(annotations[0].pageNumber);
|
||||||
this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
|
this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
deselectAnnotations(annotations: AnnotationWrapper[]) {
|
deselectAnnotations(annotations: AnnotationWrapper[]) {
|
||||||
this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)));
|
this.instance.annotManager.deselectAnnotations(
|
||||||
|
annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateToPage(pageNumber: number) {
|
navigateToPage(pageNumber: number) {
|
||||||
@ -107,7 +126,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
setInitialViewerState() {
|
setInitialViewerState() {
|
||||||
// viewer init
|
// viewer init
|
||||||
this.instance.setFitMode('FitPage');
|
this.instance.setFitMode('FitPage');
|
||||||
const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode();
|
const instanceDisplayMode = this.instance.docViewer
|
||||||
|
.getDisplayModeManager()
|
||||||
|
.getDisplayMode();
|
||||||
instanceDisplayMode.mode = 'Single';
|
instanceDisplayMode.mode = 'Single';
|
||||||
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
|
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
|
||||||
}
|
}
|
||||||
@ -127,20 +148,28 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
this._configureTextPopup();
|
this._configureTextPopup();
|
||||||
|
|
||||||
instance.annotManager.on('annotationSelected', (annotations, action) => {
|
instance.annotManager.on('annotationSelected', (annotations, action) => {
|
||||||
this.annotationSelected.emit(instance.annotManager.getSelectedAnnotations().map((ann) => ann.Id));
|
this.annotationSelected.emit(
|
||||||
|
instance.annotManager.getSelectedAnnotations().map((ann) => ann.Id)
|
||||||
|
);
|
||||||
if (action === 'deselected') {
|
if (action === 'deselected') {
|
||||||
this._toggleRectangleAnnotationAction(true);
|
this._toggleRectangleAnnotationAction(true);
|
||||||
} else {
|
} else {
|
||||||
this._configureAnnotationSpecificActions(annotations);
|
this._configureAnnotationSpecificActions(annotations);
|
||||||
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
|
this._toggleRectangleAnnotationAction(
|
||||||
|
annotations.length === 1 && annotations[0].ReadOnly
|
||||||
|
);
|
||||||
// this.annotationSelected.emit(annotations.map((a) => a.Id));
|
// this.annotationSelected.emit(annotations.map((a) => a.Id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
instance.annotManager.on('annotationChanged', (annotations) => {
|
instance.annotManager.on('annotationChanged', (annotations) => {
|
||||||
// when a rectangle is drawn, it returns one annotation with tool name 'AnnotationCreateRectangle;
|
// when a rectangle is drawn,
|
||||||
|
// it returns one annotation with tool name 'AnnotationCreateRectangle;
|
||||||
// this will auto select rectangle after drawing
|
// this will auto select rectangle after drawing
|
||||||
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
|
if (
|
||||||
|
annotations.length === 1 &&
|
||||||
|
annotations[0].ToolName === 'AnnotationCreateRectangle'
|
||||||
|
) {
|
||||||
instance.annotManager.selectAnnotations(annotations);
|
instance.annotManager.selectAnnotations(annotations);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -183,7 +212,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
|
|
||||||
instance.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
|
instance.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
|
||||||
if (event.detail.element === 'searchPanel') {
|
if (event.detail.element === 'searchPanel') {
|
||||||
const inputElement = instance.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
|
const inputElement = instance.iframeWindow.document.getElementById(
|
||||||
|
'SearchPanel__input'
|
||||||
|
) as HTMLInputElement;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
inputElement.value = '';
|
inputElement.value = '';
|
||||||
}, 0);
|
}, 0);
|
||||||
@ -253,43 +284,58 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
|
|
||||||
private _configureAnnotationSpecificActions(viewerAnnotations: Annotations.Annotation[]) {
|
private _configureAnnotationSpecificActions(viewerAnnotations: Annotations.Annotation[]) {
|
||||||
console.log('configure actions', viewerAnnotations);
|
console.log('configure actions', viewerAnnotations);
|
||||||
if (this.canPerformActions) {
|
if (!this.canPerformActions) {
|
||||||
const annotationWrappers = viewerAnnotations.map((va) => this.annotations.find((a) => a.id === va.Id)).filter((va) => !!va);
|
return;
|
||||||
this.instance.annotationPopup.update([]);
|
|
||||||
|
|
||||||
if (annotationWrappers.length === 0) {
|
|
||||||
this._configureRectangleAnnotationPopup();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add hide action as last item
|
|
||||||
const allAnnotationsHaveImageAction = annotationWrappers.reduce((acc, next) => acc && next.isImage, true);
|
|
||||||
if (allAnnotationsHaveImageAction) {
|
|
||||||
const allAreVisible = viewerAnnotations.reduce((acc, next) => next.isVisible() && acc, true);
|
|
||||||
|
|
||||||
this.instance.annotationPopup.add([
|
|
||||||
{
|
|
||||||
type: 'actionButton',
|
|
||||||
img: allAreVisible
|
|
||||||
? this._convertPath('/assets/icons/general/visibility-off.svg')
|
|
||||||
: this._convertPath('/assets/icons/general/visibility.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.hide'),
|
|
||||||
onClick: () => {
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
if (allAreVisible) {
|
|
||||||
this.instance.annotManager.hideAnnotations(viewerAnnotations);
|
|
||||||
} else {
|
|
||||||
this.instance.annotManager.showAnnotations(viewerAnnotations);
|
|
||||||
}
|
|
||||||
this.instance.annotManager.deselectAllAnnotations();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.instance.annotationPopup.add(this._annotationActionsService.getViewerAvailableActions(annotationWrappers, this.annotationsChanged));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const annotationWrappers = viewerAnnotations
|
||||||
|
.map((va) => this.annotations.find((a) => a.id === va.Id))
|
||||||
|
.filter((va) => !!va);
|
||||||
|
this.instance.annotationPopup.update([]);
|
||||||
|
|
||||||
|
if (annotationWrappers.length === 0) {
|
||||||
|
this._configureRectangleAnnotationPopup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add hide action as last item
|
||||||
|
const allAnnotationsHaveImageAction = annotationWrappers.reduce(
|
||||||
|
(acc, next) => acc && next.isImage,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (allAnnotationsHaveImageAction) {
|
||||||
|
const allAreVisible = viewerAnnotations.reduce(
|
||||||
|
(acc, next) => next.isVisible() && acc,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
this.instance.annotationPopup.add([
|
||||||
|
{
|
||||||
|
type: 'actionButton',
|
||||||
|
img: allAreVisible
|
||||||
|
? this._convertPath('/assets/icons/general/visibility-off.svg')
|
||||||
|
: this._convertPath('/assets/icons/general/visibility.svg'),
|
||||||
|
title: this._translateService.instant('annotation-actions.hide'),
|
||||||
|
onClick: () => {
|
||||||
|
this._ngZone.run(() => {
|
||||||
|
if (allAreVisible) {
|
||||||
|
this.instance.annotManager.hideAnnotations(viewerAnnotations);
|
||||||
|
} else {
|
||||||
|
this.instance.annotManager.showAnnotations(viewerAnnotations);
|
||||||
|
}
|
||||||
|
this.instance.annotManager.deselectAllAnnotations();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.instance.annotationPopup.add(
|
||||||
|
this._annotationActionsService.getViewerAvailableActions(
|
||||||
|
annotationWrappers,
|
||||||
|
this.annotationsChanged
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _configureRectangleAnnotationPopup() {
|
private _configureRectangleAnnotationPopup() {
|
||||||
@ -297,12 +343,17 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
dataElement: 'add-rectangle',
|
dataElement: 'add-rectangle',
|
||||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
||||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
|
title: this._translateService.instant(
|
||||||
|
this._manualAnnotationService.getTitle('REDACTION')
|
||||||
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedAnnotations = this.instance.annotManager.getSelectedAnnotations();
|
const selectedAnnotations = this.instance.annotManager.getSelectedAnnotations();
|
||||||
const activeAnnotation = selectedAnnotations[0];
|
const activeAnnotation = selectedAnnotations[0];
|
||||||
const activePage = selectedAnnotations[0].getPageNumber();
|
const activePage = selectedAnnotations[0].getPageNumber();
|
||||||
const quad = this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance);
|
const quad = this._annotationDrawService.annotationToQuads(
|
||||||
|
activeAnnotation,
|
||||||
|
this.instance
|
||||||
|
);
|
||||||
const quadsObject = {};
|
const quadsObject = {};
|
||||||
quadsObject[activePage] = [quad];
|
quadsObject[activePage] = [quad];
|
||||||
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
|
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
|
||||||
@ -311,7 +362,15 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
this.instance.disableElements(['shapeToolGroupButton']);
|
this.instance.disableElements(['shapeToolGroupButton']);
|
||||||
this.instance.enableElements(['shapeToolGroupButton']);
|
this.instance.enableElements(['shapeToolGroupButton']);
|
||||||
// dispatch event
|
// dispatch event
|
||||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper([quad], mre, 'REDACTION', 'RECTANGLE', activeAnnotation.Id));
|
this.manualAnnotationRequested.emit(
|
||||||
|
new ManualRedactionEntryWrapper(
|
||||||
|
[quad],
|
||||||
|
mre,
|
||||||
|
'REDACTION',
|
||||||
|
'RECTANGLE',
|
||||||
|
activeAnnotation.Id
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -344,12 +403,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
dataElement: 'add-false-positive',
|
dataElement: 'add-false-positive',
|
||||||
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
|
img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'),
|
||||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('FALSE_POSITIVE')),
|
title: this._translateService.instant(
|
||||||
|
this._manualAnnotationService.getTitle('FALSE_POSITIVE')
|
||||||
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||||
const text = this.instance.docViewer.getSelectedText();
|
const text = this.instance.docViewer.getSelectedText();
|
||||||
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
||||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'FALSE_POSITIVE'));
|
this.manualAnnotationRequested.emit(
|
||||||
|
new ManualRedactionEntryWrapper(
|
||||||
|
this.instance.docViewer.getSelectedTextQuads(),
|
||||||
|
mre,
|
||||||
|
'FALSE_POSITIVE'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -358,12 +425,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
dataElement: 'add-dictionary',
|
dataElement: 'add-dictionary',
|
||||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
|
img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'),
|
||||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')),
|
title: this._translateService.instant(
|
||||||
|
this._manualAnnotationService.getTitle('DICTIONARY')
|
||||||
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||||
const text = this.instance.docViewer.getSelectedText();
|
const text = this.instance.docViewer.getSelectedText();
|
||||||
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
||||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'DICTIONARY'));
|
this.manualAnnotationRequested.emit(
|
||||||
|
new ManualRedactionEntryWrapper(
|
||||||
|
this.instance.docViewer.getSelectedTextQuads(),
|
||||||
|
mre,
|
||||||
|
'DICTIONARY'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -371,12 +446,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
type: 'actionButton',
|
type: 'actionButton',
|
||||||
dataElement: 'add-redaction',
|
dataElement: 'add-redaction',
|
||||||
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'),
|
||||||
title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
|
title: this._translateService.instant(
|
||||||
|
this._manualAnnotationService.getTitle('REDACTION')
|
||||||
|
),
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||||
const text = this.instance.docViewer.getSelectedText();
|
const text = this.instance.docViewer.getSelectedText();
|
||||||
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
const mre = this._getManualRedactionEntry(selectedQuads, text, true);
|
||||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'REDACTION'));
|
this.manualAnnotationRequested.emit(
|
||||||
|
new ManualRedactionEntryWrapper(
|
||||||
|
this.instance.docViewer.getSelectedTextQuads(),
|
||||||
|
mre,
|
||||||
|
'REDACTION'
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this._handleCustomActions();
|
this._handleCustomActions();
|
||||||
@ -386,7 +469,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
this.instance.setToolMode('AnnotationEdit');
|
this.instance.setToolMode('AnnotationEdit');
|
||||||
if (this.canPerformActions) {
|
if (this.canPerformActions) {
|
||||||
this.instance.enableTools(['AnnotationCreateRectangle']);
|
this.instance.enableTools(['AnnotationCreateRectangle']);
|
||||||
this.instance.enableElements(['add-redaction', 'add-rectangle', 'add-false-positive', 'shapeToolGroupButton', 'annotationPopup']);
|
this.instance.enableElements([
|
||||||
|
'add-redaction',
|
||||||
|
'add-rectangle',
|
||||||
|
'add-false-positive',
|
||||||
|
'shapeToolGroupButton',
|
||||||
|
'annotationPopup'
|
||||||
|
]);
|
||||||
if (this._selectedText.length > 2) {
|
if (this._selectedText.length > 2) {
|
||||||
this.instance.enableElements(['add-dictionary', 'add-false-positive']);
|
this.instance.enableElements(['add-dictionary', 'add-false-positive']);
|
||||||
}
|
}
|
||||||
@ -403,13 +492,19 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getManualRedactionEntry(quads: any, text: string, convertQuads: boolean = false): ManualRedactionEntry {
|
private _getManualRedactionEntry(
|
||||||
|
quads: any,
|
||||||
|
text: string,
|
||||||
|
convertQuads: boolean = false
|
||||||
|
): ManualRedactionEntry {
|
||||||
text = text.replace(/-\n/gi, '');
|
text = text.replace(/-\n/gi, '');
|
||||||
const entry: ManualRedactionEntry = { positions: [] };
|
const entry: ManualRedactionEntry = { positions: [] };
|
||||||
for (const key of Object.keys(quads)) {
|
for (const key of Object.keys(quads)) {
|
||||||
for (const quad of quads[key]) {
|
for (const quad of quads[key]) {
|
||||||
const page = parseInt(key, 10);
|
const page = parseInt(key, 10);
|
||||||
entry.positions.push(this._toPosition(page, convertQuads ? this._translateQuads(page, quad) : quad));
|
entry.positions.push(
|
||||||
|
this._toPosition(page, convertQuads ? this._translateQuads(page, quad) : quad)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry.value = text;
|
entry.value = text;
|
||||||
|
|||||||
@ -53,7 +53,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="hasFiles" class="mt-24 legend pb-32">
|
<div *ngIf="hasFiles" class="mt-24 legend pb-32">
|
||||||
<div (click)="toggleFilter('needsWorkFilters', filter.key)" *ngFor="let filter of filters.needsWorkFilters" [class.active]="filter.checked">
|
<div
|
||||||
|
(click)="toggleFilter('needsWorkFilters', filter.key)"
|
||||||
|
*ngFor="let filter of filters.needsWorkFilters"
|
||||||
|
[class.active]="filter.checked"
|
||||||
|
>
|
||||||
<redaction-type-filter [filter]="filter"></redaction-type-filter>
|
<redaction-type-filter [filter]="filter"></redaction-type-filter>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -61,37 +65,63 @@
|
|||||||
<div [class.mt-24]="!hasFiles" class="pb-32 small-label stats-subtitle">
|
<div [class.mt-24]="!hasFiles" class="pb-32 small-label stats-subtitle">
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:document"></mat-icon>
|
<mat-icon svgIcon="red:document"></mat-icon>
|
||||||
<span>{{ 'project-overview.project-details.stats.documents' | translate: { count: appStateService.activeProject.files.length } }}</span>
|
<span>{{
|
||||||
|
'project-overview.project-details.stats.documents'
|
||||||
|
| translate: { count: appStateService.activeProject.files.length }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:user"></mat-icon>
|
<mat-icon svgIcon="red:user"></mat-icon>
|
||||||
<span>{{ 'project-overview.project-details.stats.people' | translate: { count: appStateService.activeProject.memberCount } }}</span>
|
<span>{{
|
||||||
|
'project-overview.project-details.stats.people'
|
||||||
|
| translate: { count: appStateService.activeProject.memberCount }
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||||
<span>{{
|
<span>{{
|
||||||
'project-overview.project-details.stats.analysed-pages' | translate: { count: appStateService.activeProject.totalNumberOfPages | number }
|
'project-overview.project-details.stats.analysed-pages'
|
||||||
|
| translate
|
||||||
|
: { count: appStateService.activeProject.totalNumberOfPages | number }
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||||
<span
|
<span
|
||||||
>{{
|
>{{
|
||||||
'project-overview.project-details.stats.created-on' | translate: { date: appStateService.activeProject.project.date | date: 'd MMM. yyyy' }
|
'project-overview.project-details.stats.created-on'
|
||||||
|
| translate
|
||||||
|
: {
|
||||||
|
date:
|
||||||
|
appStateService.activeProject.project.date
|
||||||
|
| date: 'd MMM. yyyy'
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="appStateService.activeProject.project.dueDate">
|
<div *ngIf="appStateService.activeProject.project.dueDate">
|
||||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||||
<span>{{
|
<span>{{
|
||||||
'project-overview.project-details.stats.due-date' | translate: { date: appStateService.activeProject.project.dueDate | date: 'd MMM. yyyy' }
|
'project-overview.project-details.stats.due-date'
|
||||||
|
| translate
|
||||||
|
: {
|
||||||
|
date:
|
||||||
|
appStateService.activeProject.project.dueDate
|
||||||
|
| date: 'd MMM. yyyy'
|
||||||
|
}
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-icon svgIcon="red:template"></mat-icon>
|
<mat-icon svgIcon="red:template"></mat-icon>
|
||||||
<span>{{ appStateService.getRuleSetById(appStateService.activeProject.ruleSetId)?.name }} </span>
|
<span
|
||||||
|
>{{ appStateService.getRuleSetById(appStateService.activeProject.ruleSetId)?.name }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="appStateService.activeProject.type" class="pointer" (click)="openDossierDictionaryDialog.emit()">
|
<div
|
||||||
|
*ngIf="appStateService.activeProject.type"
|
||||||
|
class="pointer"
|
||||||
|
(click)="openDossierDictionaryDialog.emit()"
|
||||||
|
>
|
||||||
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
<mat-icon svgIcon="red:dictionary"></mat-icon>
|
||||||
<span>{{ 'project-overview.project-details.dictionary' | translate }} </span>
|
<span>{{ 'project-overview.project-details.dictionary' | translate }} </span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -51,10 +51,17 @@ export class ProjectDetailsComponent implements OnInit {
|
|||||||
const groups = groupBy(this.appStateService.activeProject?.files, 'status');
|
const groups = groupBy(this.appStateService.activeProject?.files, 'status');
|
||||||
this.documentsChartData = [];
|
this.documentsChartData = [];
|
||||||
for (const key of Object.keys(groups)) {
|
for (const key of Object.keys(groups)) {
|
||||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key, key: key });
|
this.documentsChartData.push({
|
||||||
|
value: groups[key].length,
|
||||||
|
color: key,
|
||||||
|
label: key,
|
||||||
|
key: key
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
|
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
|
||||||
this.documentsChartData = this.translateChartService.translateStatus(this.documentsChartData);
|
this.documentsChartData = this.translateChartService.translateStatus(
|
||||||
|
this.documentsChartData
|
||||||
|
);
|
||||||
this._changeDetectorRef.detectChanges();
|
this._changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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