set max line length to 100 and set indent size for .json files to 2
This commit is contained in:
parent
6bf6ca70d0
commit
17979c0448
@ -8,7 +8,11 @@ indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
ij_html_quote_style = double
|
||||
max_line_length = 100
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{*.json, .prettierrc, .eslintrc}]
|
||||
indent_size = 2
|
||||
|
||||
368
.eslintrc.json
368
.eslintrc.json
@ -1,182 +1,190 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nrwl/nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nrwl/nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||
"@angular-eslint/no-host-metadata-property": "error",
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||
"@angular-eslint/no-output-native": "error",
|
||||
"@angular-eslint/no-output-on-prefix": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
"@angular-eslint/no-outputs-metadata-property": "error",
|
||||
"@angular-eslint/use-lifecycle-interface": "error",
|
||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"warn",
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"]
|
||||
},
|
||||
{
|
||||
"selector": "enumMember",
|
||||
"format": ["UPPER_CASE"]
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["protected"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private", "readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"error",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"constructor-super": "error",
|
||||
"eqeqeq": ["error", "smart"],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"import/no-deprecated": "warn",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"warn",
|
||||
"dir",
|
||||
"timeLog",
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"group",
|
||||
"groupEnd",
|
||||
"table",
|
||||
"dirxml",
|
||||
"error",
|
||||
"groupCollapsed",
|
||||
"Console",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"timeStamp",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-restricted-imports": ["error", "rxjs/Rx"],
|
||||
"no-throw-literal": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"radix": "error"
|
||||
},
|
||||
"plugins": ["eslint-plugin-import", "@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"rules": {
|
||||
"@angular-eslint/template/banana-in-box": "error",
|
||||
"@angular-eslint/template/no-negated-async": "error"
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin-template"]
|
||||
}
|
||||
]
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nrwl/nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nrwl/nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": ["plugin:@nrwl/nx/typescript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nrwl/nx/javascript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"rules": {
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||
"@angular-eslint/no-host-metadata-property": "error",
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||
"@angular-eslint/no-output-native": "error",
|
||||
"@angular-eslint/no-output-on-prefix": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
"@angular-eslint/no-outputs-metadata-property": "error",
|
||||
"@angular-eslint/use-lifecycle-interface": "error",
|
||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||
"@typescript-eslint/consistent-type-definitions": "error",
|
||||
"@typescript-eslint/dot-notation": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-types": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"warn",
|
||||
{
|
||||
"accessibility": "no-public"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "error",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"]
|
||||
},
|
||||
{
|
||||
"selector": "enumMember",
|
||||
"format": ["UPPER_CASE"]
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["protected"],
|
||||
"format": ["camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": ["private", "readonly"],
|
||||
"format": ["UPPER_CASE", "camelCase"],
|
||||
"leadingUnderscore": "require"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"error",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error",
|
||||
{
|
||||
"hoist": "all"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"constructor-super": "error",
|
||||
"eqeqeq": ["error", "smart"],
|
||||
"guard-for-in": "error",
|
||||
"id-blacklist": "off",
|
||||
"id-match": "off",
|
||||
"import/no-deprecated": "warn",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"warn",
|
||||
"dir",
|
||||
"timeLog",
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"group",
|
||||
"groupEnd",
|
||||
"table",
|
||||
"dirxml",
|
||||
"error",
|
||||
"groupCollapsed",
|
||||
"Console",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"timeStamp",
|
||||
"context"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-restricted-imports": ["error", "rxjs/Rx"],
|
||||
"no-throw-literal": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-var": "error",
|
||||
"prefer-const": "error",
|
||||
"radix": "error",
|
||||
"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"
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin-template"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -2,3 +2,5 @@
|
||||
|
||||
/dist
|
||||
/coverage
|
||||
/node_modules
|
||||
/bamboo-specs
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"useTabs": false,
|
||||
"printWidth": 160,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
@ -10,6 +10,12 @@
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.json"],
|
||||
"options": {
|
||||
"tabWidth": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
381
angular.json
381
angular.json
@ -1,194 +1,195 @@
|
||||
{
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false,
|
||||
"packageManager": "yarn"
|
||||
"$schema": "node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false,
|
||||
"packageManager": "yarn"
|
||||
},
|
||||
"defaultProject": "red-ui",
|
||||
"schematics": {
|
||||
"@nrwl/angular:application": {
|
||||
"unitTestRunner": "jest",
|
||||
"e2eTestRunner": "cypress"
|
||||
},
|
||||
"defaultProject": "red-ui",
|
||||
"schematics": {
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
}
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,37 +1,40 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": ["plugin:@nrwl/nx/angular", "plugin:@angular-eslint/template/process-inline-templates"],
|
||||
"parserOptions": {
|
||||
"project": ["apps/red-ui/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "redaction",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "redaction",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nrwl/nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"plugin:@nrwl/nx/angular",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": ["apps/red-ui/tsconfig.*?.json"]
|
||||
},
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "redaction",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "redaction",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"]
|
||||
},
|
||||
{
|
||||
"files": ["*.html"],
|
||||
"extends": ["plugin:@nrwl/nx/angular-template"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -4,7 +4,12 @@ module.exports = {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
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'
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
{
|
||||
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
"name": "app",
|
||||
"installMode": "prefetch",
|
||||
"resources": {
|
||||
"files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assets",
|
||||
"installMode": "lazy",
|
||||
"updateMode": "prefetch",
|
||||
"resources": {
|
||||
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
|
||||
}
|
||||
}
|
||||
]
|
||||
"$schema": "../../node_modules/@angular/service-worker/config/schema.json",
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
"name": "app",
|
||||
"installMode": "prefetch",
|
||||
"resources": {
|
||||
"files": ["/favicon.ico", "/index.html", "/manifest.webmanifest", "/*.css", "/*.js"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assets",
|
||||
"installMode": "lazy",
|
||||
"updateMode": "prefetch",
|
||||
"resources": {
|
||||
"files": ["/assets/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -43,7 +43,8 @@ const routes = [
|
||||
{
|
||||
path: 'main/projects',
|
||||
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',
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
<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']
|
||||
})
|
||||
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 components = [AppComponent, LogoComponent, AuthErrorComponent, ToastComponent, NotificationsComponent, ...screens];
|
||||
const components = [
|
||||
AppComponent,
|
||||
LogoComponent,
|
||||
AuthErrorComponent,
|
||||
ToastComponent,
|
||||
NotificationsComponent,
|
||||
...screens
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
@ -113,7 +120,11 @@ export class AppModule {
|
||||
|
||||
private _configureKeyCloakRouteHandling() {
|
||||
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([], {
|
||||
queryParams: {
|
||||
state: null,
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
<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
|
||||
*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"
|
||||
></p>
|
||||
<p
|
||||
|
||||
@ -11,10 +11,15 @@ export class AuthErrorComponent implements OnInit {
|
||||
configuredAdminName: string;
|
||||
configuredAdminUrl: string;
|
||||
|
||||
constructor(private readonly _userService: UserService, private readonly _appConfigService: AppConfigService) {}
|
||||
constructor(
|
||||
private readonly _userService: UserService,
|
||||
private readonly _appConfigService: AppConfigService
|
||||
) {}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -6,20 +6,36 @@
|
||||
<mat-icon svgIcon="red:menu"></mat-icon>
|
||||
</button>
|
||||
<mat-menu #menuNav="matMenu">
|
||||
<button 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>
|
||||
<button
|
||||
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 }}
|
||||
</button>
|
||||
<button
|
||||
*ngIf="appStateService.activeFile"
|
||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
|
||||
[routerLink]="
|
||||
'/main/projects/' +
|
||||
appStateService.activeProjectId +
|
||||
'/file/' +
|
||||
appStateService.activeFile.fileId
|
||||
"
|
||||
mat-menu-item
|
||||
>
|
||||
{{ appStateService.activeFile.filename }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
</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
|
||||
*ngIf="projectsView"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
@ -33,8 +49,15 @@
|
||||
{{ 'top-bar.navigation-items.back' | translate }}
|
||||
</a>
|
||||
<ng-container *ngIf="projectsView">
|
||||
<mat-icon *ngIf="!appStateService.activeProject" class="primary" svgIcon="red:arrow-down"></mat-icon>
|
||||
<mat-icon *ngIf="appStateService.activeProject" svgIcon="red:arrow-right"></mat-icon>
|
||||
<mat-icon
|
||||
*ngIf="!appStateService.activeProject"
|
||||
class="primary"
|
||||
svgIcon="red:arrow-down"
|
||||
></mat-icon>
|
||||
<mat-icon
|
||||
*ngIf="appStateService.activeProject"
|
||||
svgIcon="red:arrow-right"
|
||||
></mat-icon>
|
||||
<a
|
||||
*ngIf="appStateService.activeProject"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
@ -47,7 +70,12 @@
|
||||
<mat-icon *ngIf="appStateService.activeFile" svgIcon="red:arrow-right"></mat-icon>
|
||||
<a
|
||||
*ngIf="appStateService.activeFile"
|
||||
[routerLink]="'/main/projects/' + appStateService.activeProjectId + '/file/' + appStateService.activeFile.fileId"
|
||||
[routerLink]="
|
||||
'/main/projects/' +
|
||||
appStateService.activeProjectId +
|
||||
'/file/' +
|
||||
appStateService.activeFile.fileId
|
||||
"
|
||||
class="breadcrumb"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
@ -60,13 +88,28 @@
|
||||
<redaction-logo></redaction-logo>
|
||||
</redaction-hidden-action>
|
||||
<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 class="menu right flex-2">
|
||||
<redaction-notifications *ngIf="userPreferenceService.areDevFeaturesEnabled" class="mr-8"></redaction-notifications>
|
||||
<redaction-user-button [matMenuTriggerFor]="userMenu" [showDot]="showPendingDownloadsDot" [user]="user"></redaction-user-button>
|
||||
<redaction-notifications
|
||||
*ngIf="userPreferenceService.areDevFeaturesEnabled"
|
||||
class="mr-8"
|
||||
></redaction-notifications>
|
||||
<redaction-user-button
|
||||
[matMenuTriggerFor]="userMenu"
|
||||
[showDot]="showPendingDownloadsDot"
|
||||
[user]="user"
|
||||
></redaction-user-button>
|
||||
<mat-menu #userMenu="matMenu" class="user-menu" xPosition="before">
|
||||
<button [routerLink]="'/main/my-profile'" mat-menu-item translate="top-bar.navigation-items.my-account.children.my-profile"></button>
|
||||
<button
|
||||
[routerLink]="'/main/my-profile'"
|
||||
mat-menu-item
|
||||
translate="top-bar.navigation-items.my-account.children.my-profile"
|
||||
></button>
|
||||
<button
|
||||
(click)="appStateService.reset()"
|
||||
*ngIf="permissionsService.isManager() || permissionsService.isUserAdmin()"
|
||||
|
||||
@ -14,26 +14,49 @@
|
||||
<div class="content-container">
|
||||
<div class="header-item">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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 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>
|
||||
<redaction-table-col-name
|
||||
label="downloads-list.table-col-names.name"
|
||||
></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 class="scrollbar-placeholder"></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>
|
||||
<!-- Table lines -->
|
||||
<div *cdkVirtualFor="let download of fileDownloadService.downloads" class="table-item">
|
||||
<div
|
||||
*cdkVirtualFor="let download of fileDownloadService.downloads"
|
||||
class="table-item"
|
||||
>
|
||||
<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 class="small-label">
|
||||
@ -61,7 +84,12 @@
|
||||
>
|
||||
</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>
|
||||
|
||||
<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']
|
||||
})
|
||||
export class DownloadsListScreenComponent implements OnInit {
|
||||
constructor(readonly fileDownloadService: FileDownloadService, private readonly _downloadControllerService: DownloadControllerService) {}
|
||||
constructor(
|
||||
readonly fileDownloadService: FileDownloadService,
|
||||
private readonly _downloadControllerService: DownloadControllerService
|
||||
) {}
|
||||
|
||||
get noData(): boolean {
|
||||
return this.fileDownloadService.downloads.length === 0;
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
<redaction-circle-button [matMenuTriggerFor]="overlay" [showDot]="hasUnread" icon="red:notification"></redaction-circle-button>
|
||||
<mat-menu #overlay="matMenu" backdropClass="notifications-backdrop" class="notifications-menu" xPosition="before">
|
||||
<redaction-circle-button
|
||||
[matMenuTriggerFor]="overlay"
|
||||
[showDot]="hasUnread"
|
||||
icon="red:notification"
|
||||
></redaction-circle-button>
|
||||
<mat-menu
|
||||
#overlay="matMenu"
|
||||
backdropClass="notifications-backdrop"
|
||||
class="notifications-menu"
|
||||
xPosition="before"
|
||||
>
|
||||
<div *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
|
||||
<div class="all-caps-label">{{ day(group) }}</div>
|
||||
<div
|
||||
@ -15,7 +24,10 @@
|
||||
</div>
|
||||
<div
|
||||
(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"
|
||||
matTooltipPosition="before"
|
||||
></div>
|
||||
|
||||
@ -2,8 +2,20 @@
|
||||
<div *ngIf="title" [attr.aria-label]="title" [class]="options.titleClass">
|
||||
{{ title }}
|
||||
</div>
|
||||
<div *ngIf="message && options.enableHtml" [class]="options.messageClass" [innerHTML]="message" aria-live="polite" role="alert"></div>
|
||||
<div *ngIf="message && !options.enableHtml" [attr.aria-label]="message" [class]="options.messageClass" aria-live="polite" role="alert">
|
||||
<div
|
||||
*ngIf="message && options.enableHtml"
|
||||
[class]="options.messageClass"
|
||||
[innerHTML]="message"
|
||||
aria-live="polite"
|
||||
role="alert"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="message && !options.enableHtml"
|
||||
[attr.aria-label]="message"
|
||||
[class]="options.messageClass"
|
||||
aria-live="polite"
|
||||
role="alert"
|
||||
>
|
||||
{{ message }}
|
||||
</div>
|
||||
|
||||
|
||||
@ -7,7 +7,10 @@ import { Toast, ToastPackage, ToastrService } from 'ngx-toastr';
|
||||
styleUrls: ['./toast.component.scss']
|
||||
})
|
||||
export class ToastComponent extends Toast {
|
||||
constructor(protected readonly _toastrService: ToastrService, readonly toastPackage: ToastPackage) {
|
||||
constructor(
|
||||
protected readonly _toastrService: ToastrService,
|
||||
readonly toastPackage: ToastPackage
|
||||
) {
|
||||
super(_toastrService, toastPackage);
|
||||
}
|
||||
|
||||
|
||||
@ -23,17 +23,29 @@
|
||||
<input formControlName="lastName" name="lastName" type="text" />
|
||||
</div>
|
||||
<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-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-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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 }}
|
||||
</button>
|
||||
</div>
|
||||
@ -42,4 +54,6 @@
|
||||
</div>
|
||||
</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
|
||||
return !component.hasChanges
|
||||
? true
|
||||
: // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
|
||||
// when navigating away from your angular app, the browser will show a generic warning message
|
||||
: // NOTE: this warning message will only be shown when navigating elsewhere
|
||||
// within your angular app;
|
||||
// when navigating away from your angular app,
|
||||
// the browser will show a generic warning message
|
||||
// see http://stackoverflow.com/a/42207299/7307355
|
||||
confirm(this._translateService.instant('pending-changes-guard'));
|
||||
}
|
||||
|
||||
@ -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 { from, of } from 'rxjs';
|
||||
import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
@ -7,7 +13,11 @@ import { AppLoadStateService } from '@services/app-load-state.service';
|
||||
providedIn: 'root'
|
||||
})
|
||||
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> {
|
||||
this._appLoadStateService.pushLoadingEvent(true);
|
||||
@ -21,7 +31,10 @@ export class CompositeRouteGuard implements CanActivate {
|
||||
if (canActivateResult instanceof Promise) {
|
||||
canActivateResult = from(canActivateResult);
|
||||
}
|
||||
if (typeof canActivateResult === 'boolean' || canActivateResult instanceof UrlTree) {
|
||||
if (
|
||||
typeof canActivateResult === 'boolean' ||
|
||||
canActivateResult instanceof UrlTree
|
||||
) {
|
||||
canActivateResult = of(canActivateResult);
|
||||
}
|
||||
|
||||
|
||||
@ -30,22 +30,34 @@ export class AnnotationPermissions {
|
||||
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
||||
|
||||
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.canAcceptRecommendation = annotation.isRecommendation;
|
||||
|
||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive && !annotation.force;
|
||||
permissions.canMarkTextOnlyAsFalsePositive = annotation.canBeMarkedAsFalsePositiveWithTextOnly && !annotation.force;
|
||||
permissions.canMarkAsFalsePositive =
|
||||
annotation.canBeMarkedAsFalsePositive && !annotation.force;
|
||||
permissions.canMarkTextOnlyAsFalsePositive =
|
||||
annotation.canBeMarkedAsFalsePositiveWithTextOnly && !annotation.force;
|
||||
|
||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted && !annotation.force;
|
||||
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 =
|
||||
isApprover && (annotation.isSuggestion || (annotation.isReadyForAnalysis && !permissions.canUndo && annotation.superType !== 'pending-analysis'));
|
||||
isApprover &&
|
||||
(annotation.isSuggestion ||
|
||||
(annotation.isReadyForAnalysis &&
|
||||
!permissions.canUndo &&
|
||||
annotation.superType !== 'pending-analysis'));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@ -79,15 +79,28 @@ export class AnnotationWrapper {
|
||||
}
|
||||
|
||||
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() {
|
||||
return !this.canBeMarkedAsFalsePositive && (this.isRecommendation || this.superType === 'redaction') && !this.isImage;
|
||||
return (
|
||||
!this.canBeMarkedAsFalsePositive &&
|
||||
(this.isRecommendation || this.superType === 'redaction') &&
|
||||
!this.isImage
|
||||
);
|
||||
}
|
||||
|
||||
get isSuperTypeBasedColor() {
|
||||
return this.isSkipped || this.isSuggestion || this.isReadyForAnalysis || this.isDeclinedSuggestion;
|
||||
return (
|
||||
this.isSkipped ||
|
||||
this.isSuggestion ||
|
||||
this.isReadyForAnalysis ||
|
||||
this.isDeclinedSuggestion
|
||||
);
|
||||
}
|
||||
|
||||
get isSkipped() {
|
||||
@ -104,7 +117,10 @@ export class AnnotationWrapper {
|
||||
|
||||
get isFalsePositive() {
|
||||
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() {
|
||||
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() {
|
||||
return this.superType === 'suggestion-remove' || this.superType === 'suggestion-remove-dictionary';
|
||||
return (
|
||||
this.superType === 'suggestion-remove' ||
|
||||
this.superType === 'suggestion-remove-dictionary'
|
||||
);
|
||||
}
|
||||
|
||||
get isModifyDictionary() {
|
||||
@ -154,7 +177,10 @@ export class AnnotationWrapper {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -203,13 +229,21 @@ export class AnnotationWrapper {
|
||||
return annotationWrapper;
|
||||
}
|
||||
|
||||
private static _handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: RedactionLogEntryWrapper) {
|
||||
private static _handleRecommendations(
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
redactionLogEntry: RedactionLogEntryWrapper
|
||||
) {
|
||||
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.redacted) {
|
||||
annotationWrapper.superType = 'recommendation';
|
||||
@ -235,23 +269,40 @@ export class AnnotationWrapper {
|
||||
} else {
|
||||
if (redactionLogEntryWrapper.redacted) {
|
||||
if (redactionLogEntryWrapper.force) {
|
||||
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'redaction';
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.status === 'REQUESTED'
|
||||
? 'suggestion-add'
|
||||
: 'redaction';
|
||||
} else if (redactionLogEntryWrapper.type === 'manual') {
|
||||
annotationWrapper.superType = redactionLogEntryWrapper.status === 'REQUESTED' ? 'suggestion-add' : 'manual-redaction';
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.status === 'REQUESTED'
|
||||
? 'suggestion-add'
|
||||
: 'manual-redaction';
|
||||
} else {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
if (redactionLogEntryWrapper.dictionaryEntry) {
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'suggestion-add-dictionary' : 'suggestion-remove-dictionary';
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'suggestion-add-dictionary'
|
||||
: 'suggestion-remove-dictionary';
|
||||
} 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.dictionaryEntry) {
|
||||
annotationWrapper.superType = redactionLogEntryWrapper.manualRedactionType === 'ADD' ? 'add-dictionary' : 'remove-dictionary';
|
||||
annotationWrapper.superType =
|
||||
redactionLogEntryWrapper.manualRedactionType === 'ADD'
|
||||
? 'add-dictionary'
|
||||
: 'remove-dictionary';
|
||||
} 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) {
|
||||
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.userId) {
|
||||
if (redactionLogEntryWrapper.manualRedactionType === 'REMOVE' || redactionLogEntryWrapper.manualRedactionType === 'UNDO') {
|
||||
if (
|
||||
redactionLogEntryWrapper.manualRedactionType === 'REMOVE' ||
|
||||
redactionLogEntryWrapper.manualRedactionType === 'UNDO'
|
||||
) {
|
||||
annotationWrapper.superType = 'pending-analysis';
|
||||
return;
|
||||
}
|
||||
@ -283,7 +344,10 @@ export class AnnotationWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
||||
private static _createContent(
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
entry: RedactionLogEntryWrapper
|
||||
) {
|
||||
let content = '';
|
||||
if (entry.matchedRule) {
|
||||
content += 'Rule ' + entry.matchedRule + ' matched \n\n';
|
||||
|
||||
@ -33,7 +33,12 @@ export class FileDataModel {
|
||||
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);
|
||||
let allAnnotations = entries.map((entry) => AnnotationWrapper.fromData(entry));
|
||||
|
||||
@ -70,11 +75,14 @@ export class FileDataModel {
|
||||
return;
|
||||
}
|
||||
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
||||
actionPendingReanalysis: false
|
||||
};
|
||||
|
||||
Object.assign(redactionLogEntryWrapper, changeLogEntry);
|
||||
|
||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
redactionLogEntryWrapper.comments =
|
||||
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
redactionLogEntryWrapper.isChangeLogEntry = true;
|
||||
redactionLogEntryWrapper.changeLogType = changeLogEntry.changeType;
|
||||
redactionLogEntryWrapper.id = 'changed-log-removed-' + redactionLogEntryWrapper.id;
|
||||
@ -88,12 +96,17 @@ export class FileDataModel {
|
||||
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
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {
|
||||
actionPendingReanalysis: false
|
||||
};
|
||||
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
redactionLogEntryWrapper.comments =
|
||||
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
redactionLogEntryWrapper.isChangeLogEntry = !!existingChangeLogEntry;
|
||||
redactionLogEntryWrapper.changeLogType = 'ADDED';
|
||||
result.push(redactionLogEntryWrapper);
|
||||
@ -117,7 +130,10 @@ export class FileDataModel {
|
||||
relevantRedactionLogEntry.force = true;
|
||||
|
||||
// if statuses differ
|
||||
if (!forceRedaction.processedDate || forceRedaction.status !== relevantRedactionLogEntry.status) {
|
||||
if (
|
||||
!forceRedaction.processedDate ||
|
||||
forceRedaction.status !== relevantRedactionLogEntry.status
|
||||
) {
|
||||
relevantRedactionLogEntry.actionPendingReanalysis = true;
|
||||
relevantRedactionLogEntry.status = forceRedaction.status;
|
||||
}
|
||||
@ -152,7 +168,8 @@ export class FileDataModel {
|
||||
relevantRedactionLogEntry.status = manual.status;
|
||||
}
|
||||
} 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)) {
|
||||
return;
|
||||
}
|
||||
@ -175,9 +192,11 @@ export class FileDataModel {
|
||||
redactionLogEntryWrapper.hint = dictionary.hint;
|
||||
redactionLogEntryWrapper.manualRedactionType = 'ADD';
|
||||
redactionLogEntryWrapper.manual = true;
|
||||
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
redactionLogEntryWrapper.comments =
|
||||
this.manualRedactions.comments[redactionLogEntryWrapper.id];
|
||||
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;
|
||||
}
|
||||
|
||||
@ -212,14 +231,18 @@ export class FileDataModel {
|
||||
result.forEach((redactionLogEntry) => {
|
||||
if (redactionLogEntry.manual) {
|
||||
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
|
||||
if (!foundManualEntry) {
|
||||
redactionLogEntry.hidden = true;
|
||||
}
|
||||
}
|
||||
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
|
||||
if (!foundManualEntry) {
|
||||
redactionLogEntry.manual = false;
|
||||
@ -236,6 +259,9 @@ export class FileDataModel {
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
if (fileAttributesConfig) {
|
||||
const primary = fileAttributesConfig.fileAttributeConfigs?.find((c) => c.primaryAttribute);
|
||||
const primary = fileAttributesConfig.fileAttributeConfigs?.find(
|
||||
(c) => c.primaryAttribute
|
||||
);
|
||||
if (primary && fileStatus.fileAttributes?.attributeIdToValue) {
|
||||
this.primaryAttribute = fileStatus.fileAttributes?.attributeIdToValue[primary.id];
|
||||
this.searchField += ' ' + this.primaryAttribute;
|
||||
@ -125,7 +132,9 @@ export class FileStatusWrapper {
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -187,6 +196,8 @@ export class FileStatusWrapper {
|
||||
get newestDate() {
|
||||
const uploadedDate = new Date(this.fileStatus.lastUploaded);
|
||||
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;
|
||||
commentId;
|
||||
|
||||
constructor(public manualRedactionEntryWrapper: ManualRedactionEntryWrapper, public manualAddResponse: ManualAddResponse) {
|
||||
this.annotationId = manualAddResponse?.annotationId ? manualAddResponse.annotationId : new Date().getTime();
|
||||
this.commentId = manualAddResponse?.commentId ? manualAddResponse.commentId : new Date().getTime();
|
||||
constructor(
|
||||
public manualRedactionEntryWrapper: ManualRedactionEntryWrapper,
|
||||
public manualAddResponse: ManualAddResponse
|
||||
) {
|
||||
this.annotationId = manualAddResponse?.annotationId
|
||||
? manualAddResponse.annotationId
|
||||
: new Date().getTime();
|
||||
this.commentId = manualAddResponse?.commentId
|
||||
? manualAddResponse.commentId
|
||||
: new Date().getTime();
|
||||
}
|
||||
|
||||
get dictionary() {
|
||||
|
||||
@ -77,6 +77,13 @@ const components = [
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
providers: [AdminDialogService],
|
||||
imports: [CommonModule, SharedModule, AdminRoutingModule, AceEditorModule, NgxChartsModule, ColorPickerModule]
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
AdminRoutingModule,
|
||||
AceEditorModule,
|
||||
NgxChartsModule,
|
||||
ColorPickerModule
|
||||
]
|
||||
})
|
||||
export class AdminModule {}
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
<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">
|
||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||
@ -15,7 +20,12 @@
|
||||
<ng-container *ngIf="appStateService.activeDictionary">
|
||||
<mat-icon svgIcon="red:arrow-right"></mat-icon>
|
||||
<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"
|
||||
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 { 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({
|
||||
// eslint-disable-next-line @angular-eslint/component-selector
|
||||
@ -261,7 +277,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
if (this.bandwidth === undefined) {
|
||||
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') {
|
||||
scale = scaleTime().range([0, width]).domain(domain);
|
||||
@ -326,7 +344,12 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
domain = this.yDomain;
|
||||
}
|
||||
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() {
|
||||
@ -364,7 +387,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
}
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@ -374,7 +399,9 @@ export class ComboChartComponent extends BaseChartComponent {
|
||||
}
|
||||
|
||||
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 = [...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 { formatLabel } from '@swimlane/ngx-charts';
|
||||
|
||||
@ -151,7 +158,10 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
||||
bar.gradientStops = this.colors.getLinearGradientStops(value);
|
||||
} else {
|
||||
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;
|
||||
bar.tooltipText = `
|
||||
<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;
|
||||
@ -177,7 +189,9 @@ export class ComboSeriesVerticalComponent implements OnChanges {
|
||||
|
||||
isActive(entry): boolean {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,15 @@ import { AppStateService } from '@state/app-state.service';
|
||||
export class SideNavComponent {
|
||||
@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: [
|
||||
{ screen: 'project-templates', onlyAdmin: true },
|
||||
{ screen: 'digital-signature', onlyAdmin: true },
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
<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>
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<section class="dialog">
|
||||
<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>
|
||||
|
||||
<form (submit)="saveDictionary()" [formGroup]="dictionaryForm">
|
||||
@ -15,13 +18,23 @@
|
||||
<div class="first-row">
|
||||
<div *ngIf="!dictionary" class="red-input-group required">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group required w-75">
|
||||
<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 class="red-input-group required">
|
||||
@ -41,7 +54,10 @@
|
||||
class="input-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"
|
||||
></mat-icon>
|
||||
</div>
|
||||
@ -53,7 +69,9 @@
|
||||
<textarea
|
||||
formControlName="description"
|
||||
name="description"
|
||||
placeholder="{{ 'add-edit-dictionary.form.description-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'add-edit-dictionary.form.description-placeholder' | translate
|
||||
}}"
|
||||
redactionHasScrollbar
|
||||
rows="4"
|
||||
type="text"
|
||||
@ -62,8 +80,12 @@
|
||||
|
||||
<div class="red-input-group slider-row">
|
||||
<mat-button-toggle-group appearance="legacy" formControlName="hint" name="hint">
|
||||
<mat-button-toggle [value]="false"> {{ 'add-edit-dictionary.form.redaction' | translate }}</mat-button-toggle>
|
||||
<mat-button-toggle [value]="true"> {{ 'add-edit-dictionary.form.hint' | translate }}</mat-button-toggle>
|
||||
<mat-button-toggle [value]="false">
|
||||
{{ 'add-edit-dictionary.form.redaction' | translate }}</mat-button-toggle
|
||||
>
|
||||
<mat-button-toggle [value]="true">
|
||||
{{ 'add-edit-dictionary.form.hint' | translate }}</mat-button-toggle
|
||||
>
|
||||
</mat-button-toggle-group>
|
||||
</div>
|
||||
|
||||
@ -74,18 +96,31 @@
|
||||
</div>
|
||||
|
||||
<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 }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 }}
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@ -65,7 +65,11 @@ export class AddEditDictionaryDialogComponent {
|
||||
let observable: Observable<any>;
|
||||
if (this.dictionary) {
|
||||
// edit mode
|
||||
observable = this._dictionaryControllerService.updateType(typeValue, typeValue.type, this._ruleSetId);
|
||||
observable = this._dictionaryControllerService.updateType(
|
||||
typeValue,
|
||||
typeValue.type,
|
||||
this._ruleSetId
|
||||
);
|
||||
} else {
|
||||
// create mode
|
||||
typeValue.ruleSetId = this._ruleSetId;
|
||||
@ -89,7 +93,11 @@ export class AddEditDictionaryDialogComponent {
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@ -1,13 +1,23 @@
|
||||
<section class="dialog">
|
||||
<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>
|
||||
|
||||
<form (submit)="saveFileAttribute()" [formGroup]="fileAttributeForm">
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-file-attribute.form.name"></label>
|
||||
<input formControlName="label" name="label" 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 class="red-input-group required w-300">
|
||||
@ -15,7 +25,9 @@
|
||||
<input
|
||||
formControlName="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"
|
||||
/>
|
||||
</div>
|
||||
@ -31,22 +43,37 @@
|
||||
|
||||
<div class="options-wrapper">
|
||||
<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 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 }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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 }}
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@ -13,13 +13,18 @@ export class AddEditFileAttributeDialogComponent {
|
||||
fileAttributeForm: FormGroup;
|
||||
fileAttribute: FileAttributeConfig;
|
||||
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(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
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.ruleSetId = data.ruleSetId;
|
||||
@ -27,7 +32,10 @@ export class AddEditFileAttributeDialogComponent {
|
||||
this.fileAttributeForm = this._formBuilder.group({
|
||||
label: [this.fileAttribute?.label, 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],
|
||||
primaryAttribute: [this.fileAttribute?.primaryAttribute]
|
||||
});
|
||||
|
||||
@ -1,13 +1,25 @@
|
||||
<section class="dialog">
|
||||
<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>
|
||||
|
||||
<form (submit)="saveRuleSet()" [formGroup]="ruleSetForm">
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="add-edit-project-template.form.name"></label>
|
||||
<input formControlName="name" name="name" 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 class="red-input-group w-400">
|
||||
@ -15,7 +27,9 @@
|
||||
<textarea
|
||||
formControlName="description"
|
||||
name="description"
|
||||
placeholder="{{ 'add-edit-project-template.form.description-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'add-edit-project-template.form.description-placeholder' | translate
|
||||
}}"
|
||||
rows="4"
|
||||
type="text"
|
||||
></textarea>
|
||||
@ -23,11 +37,21 @@
|
||||
|
||||
<div class="validity">
|
||||
<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 }}
|
||||
</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 }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -35,7 +59,11 @@
|
||||
<div>
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<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-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
@ -45,7 +73,11 @@
|
||||
|
||||
<div class="red-input-group datepicker-wrapper">
|
||||
<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-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
@ -58,14 +90,25 @@
|
||||
<p class="download-includes">{{ 'download-includes' | translate }}</p>
|
||||
<div class="space-between">
|
||||
<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"
|
||||
[translatePrefix]="'report-type.'"
|
||||
class="w-410"
|
||||
formControlName="reportTypes"
|
||||
></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"
|
||||
[translatePrefix]="'download-type.'"
|
||||
class="w-410"
|
||||
@ -75,11 +118,20 @@
|
||||
</div>
|
||||
|
||||
<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 }}
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@ -32,13 +32,25 @@ export class AddEditRuleSetDialogComponent {
|
||||
this.ruleSetForm = this._formBuilder.group({
|
||||
name: [this.ruleSet?.name, Validators.required],
|
||||
description: [this.ruleSet?.description],
|
||||
validFrom: [this.ruleSet?.validFrom ? moment(this.ruleSet?.validFrom) : null, 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']],
|
||||
validFrom: [
|
||||
this.ruleSet?.validFrom ? moment(this.ruleSet?.validFrom) : null,
|
||||
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: [
|
||||
this.ruleSet
|
||||
? 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
|
||||
]
|
||||
});
|
||||
@ -61,14 +73,24 @@ export class AddEditRuleSetDialogComponent {
|
||||
if (this.hasValidFrom !== !!this.ruleSet.validFrom) {
|
||||
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;
|
||||
}
|
||||
} else if (key === 'validTo') {
|
||||
if (this.hasValidTo !== !!this.ruleSet.validTo) {
|
||||
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;
|
||||
}
|
||||
} else if (this.ruleSet[key] !== this.ruleSetForm.get(key).value) {
|
||||
@ -93,7 +115,16 @@ export class AddEditRuleSetDialogComponent {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,11 @@
|
||||
<div class="red-input-group">
|
||||
<label translate="add-edit-user.form.role"></label>
|
||||
<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 }}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
@ -36,15 +40,37 @@
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button [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
|
||||
[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>
|
||||
|
||||
<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>
|
||||
</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>
|
||||
|
||||
@ -29,7 +29,10 @@ export class AddEditUserDialogComponent {
|
||||
disabled:
|
||||
this.user &&
|
||||
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
|
||||
)
|
||||
}
|
||||
@ -40,7 +43,10 @@ export class AddEditUserDialogComponent {
|
||||
this.userForm = this._formBuilder.group({
|
||||
firstName: [this.user?.firstName, 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],
|
||||
...rolesControls
|
||||
});
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<section class="dialog">
|
||||
<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 *ngIf="showToast" class="inline-dialog-toast toast-error">
|
||||
@ -27,7 +30,15 @@
|
||||
<button (click)="deleteFileAttribute()" color="primary" mat-flat-button>
|
||||
{{ 'confirm-delete-file-attribute.delete.' + type | translate }}
|
||||
</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>
|
||||
<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>
|
||||
|
||||
@ -17,7 +17,10 @@
|
||||
[class.error]="!checkbox.value && showToast"
|
||||
color="primary"
|
||||
>
|
||||
{{ 'confirm-delete-users.' + checkbox.label | translate: { projectsCount: projectsCount } }}
|
||||
{{
|
||||
'confirm-delete-users.' + checkbox.label
|
||||
| translate: { projectsCount: projectsCount }
|
||||
}}
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
|
||||
@ -25,7 +28,15 @@
|
||||
<button (click)="deleteUser()" color="primary" mat-flat-button>
|
||||
{{ 'confirm-delete-users.delete.' + type | translate }}
|
||||
</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>
|
||||
<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>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
<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">
|
||||
<div class="dialog-content">
|
||||
@ -19,17 +22,32 @@
|
||||
[style.background]="colorForm.get('color').value"
|
||||
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 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 }}
|
||||
</button>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
@ -23,7 +23,8 @@ export class EditColorDialogComponent {
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
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.colorKey = data.colorKey;
|
||||
@ -50,11 +51,17 @@ export class EditColorDialogComponent {
|
||||
this.dialogRef.close(true);
|
||||
this._notificationService.showToastNotification(
|
||||
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) {
|
||||
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>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<ng-container *ngIf="areSomeEntitiesSelected">
|
||||
@ -29,7 +32,10 @@
|
||||
|
||||
<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">
|
||||
<button
|
||||
@ -45,7 +51,11 @@
|
||||
</mat-menu>
|
||||
|
||||
<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 }}
|
||||
</button>
|
||||
</mat-menu>
|
||||
@ -55,9 +65,14 @@
|
||||
<div class="table-header" redactionSyncWidth="table-item">
|
||||
<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
|
||||
class="flex-center"
|
||||
@ -76,7 +91,11 @@
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
|
||||
<redaction-empty-state *ngIf="!allEntities.length" icon="red:attribute" screen="file-attributes-csv-import"></redaction-empty-state>
|
||||
<redaction-empty-state
|
||||
*ngIf="!allEntities.length"
|
||||
icon="red:attribute"
|
||||
screen="file-attributes-csv-import"
|
||||
></redaction-empty-state>
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="50" redactionHasScrollbar>
|
||||
<!-- Table lines -->
|
||||
@ -93,7 +112,10 @@
|
||||
<div *ngIf="!field.editingName">
|
||||
{{ field.name }}
|
||||
</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">
|
||||
<input [(ngModel)]="field.temporaryName" name="name" />
|
||||
</div>
|
||||
@ -139,7 +161,10 @@
|
||||
<mat-slide-toggle [(ngModel)]="field.readonly" color="primary"></mat-slide-toggle>
|
||||
</div>
|
||||
<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 class="actions-container">
|
||||
<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 { Field } from '../file-attributes-csv-import-dialog.component';
|
||||
import { FileAttributeConfig } from '@redaction/red-ui-http';
|
||||
@ -14,7 +22,11 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
|
||||
@Output() setHoveredColumn = new EventEmitter<string>();
|
||||
@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';
|
||||
|
||||
@ -30,7 +42,9 @@ export class ActiveFieldsListingComponent extends BaseListingComponent<Field> im
|
||||
}
|
||||
|
||||
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.allEntitiesChange.emit(this.allEntities);
|
||||
this.selectedEntitiesIds = [];
|
||||
|
||||
@ -4,25 +4,37 @@
|
||||
<div class="dialog-content">
|
||||
<div class="sub-header">
|
||||
<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">
|
||||
{{ '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 class="right">
|
||||
<form (submit)="changedParseConfig && readFile()" [formGroup]="baseConfigForm">
|
||||
<div class="red-input-group required w-250">
|
||||
<mat-form-field floatLabel="always">
|
||||
<mat-label>{{ 'file-attributes-csv-import.key-column' | translate }}</mat-label>
|
||||
<mat-label>{{
|
||||
'file-attributes-csv-import.key-column' | translate
|
||||
}}</mat-label>
|
||||
<input
|
||||
[matAutocomplete]="auto"
|
||||
[placeholder]="'file-attributes-csv-import.key-column-placeholder' | translate"
|
||||
[placeholder]="
|
||||
'file-attributes-csv-import.key-column-placeholder' | translate
|
||||
"
|
||||
formControlName="filenameMappingColumnHeaderName"
|
||||
matInput
|
||||
type="text"
|
||||
/>
|
||||
<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 }}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
@ -32,7 +44,9 @@
|
||||
<div class="red-input-group required w-110">
|
||||
<label translate="file-attributes-csv-import.delimiter"></label>
|
||||
<input
|
||||
[placeholder]="'file-attributes-csv-import.delimiter-placeholder' | translate"
|
||||
[placeholder]="
|
||||
'file-attributes-csv-import.delimiter-placeholder' | translate
|
||||
"
|
||||
formControlName="delimiter"
|
||||
name="delimiter"
|
||||
type="text"
|
||||
@ -42,7 +56,9 @@
|
||||
<div class="red-input-group required w-160">
|
||||
<label translate="file-attributes-csv-import.encoding"></label>
|
||||
<input
|
||||
[placeholder]="'file-attributes-csv-import.encoding-placeholder' | translate"
|
||||
[placeholder]="
|
||||
'file-attributes-csv-import.encoding-placeholder' | translate
|
||||
"
|
||||
formControlName="encoding"
|
||||
name="encoding"
|
||||
type="text"
|
||||
@ -62,9 +78,13 @@
|
||||
<div class="csv-part-header">
|
||||
<div>
|
||||
<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 class="all-caps-label">{{ 'file-attributes-csv-import.selected' | translate: { value: activeFields.length } }}</span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<redaction-circle-button
|
||||
@ -106,17 +126,29 @@
|
||||
{{ field.csvColumn }}
|
||||
</div>
|
||||
<div class="secondary">
|
||||
<div class="entry-count small-label">{{ getEntries(field.csvColumn) }} entries</div>
|
||||
<div class="sample small-label">Sample: {{ getSample(field.csvColumn) }}</div>
|
||||
<div class="entry-count small-label">
|
||||
{{ getEntries(field.csvColumn) }} entries
|
||||
</div>
|
||||
<div class="sample small-label">
|
||||
Sample: {{ getSample(field.csvColumn) }}
|
||||
</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="all-caps-label">
|
||||
{{ 'file-attributes-csv-import.csv-column' + (previewExpanded ? '' : '-preview') | translate }}
|
||||
{{
|
||||
'file-attributes-csv-import.csv-column' +
|
||||
(previewExpanded ? '' : '-preview') | translate
|
||||
}}
|
||||
</div>
|
||||
<redaction-circle-button
|
||||
(click)="previewExpanded = !previewExpanded"
|
||||
@ -124,9 +156,16 @@
|
||||
></redaction-circle-button>
|
||||
</div>
|
||||
<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">
|
||||
{{ 'file-attributes-csv-import.no-sample-data-for' | translate: { column: hoveredColumn } }}
|
||||
{{
|
||||
'file-attributes-csv-import.no-sample-data-for'
|
||||
| translate: { column: hoveredColumn }
|
||||
}}
|
||||
</div>
|
||||
<div *ngFor="let row of columnSample">
|
||||
{{ row }}
|
||||
@ -144,12 +183,23 @@
|
||||
</div>
|
||||
|
||||
<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 }}
|
||||
</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>
|
||||
|
||||
<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>
|
||||
|
||||
@ -3,7 +3,11 @@ import { AbstractControl, FormGroup, ValidatorFn, Validators } from '@angular/fo
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import * as Papa from 'papaparse';
|
||||
import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http';
|
||||
import {
|
||||
FileAttributeConfig,
|
||||
FileAttributesConfig,
|
||||
FileAttributesControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
@ -40,7 +44,8 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
keepPreview = false;
|
||||
columnSample = [];
|
||||
initialParseConfig: { delimiter?: string; encoding?: string } = {};
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false }) cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: false })
|
||||
cdkVirtualScrollViewport: CdkVirtualScrollViewport;
|
||||
protected readonly _searchKey = 'csvColumn';
|
||||
|
||||
constructor(
|
||||
@ -50,14 +55,18 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
private readonly _notificationService: NotificationService,
|
||||
public dialogRef: MatDialogRef<FileAttributesCsvImportDialogComponent>,
|
||||
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);
|
||||
this.csvFile = data.csv;
|
||||
this.ruleSetId = data.ruleSetId;
|
||||
|
||||
this.baseConfigForm = this._formBuilder.group({
|
||||
filenameMappingColumnHeaderName: ['', [Validators.required, this._autocompleteStringValidator()]],
|
||||
filenameMappingColumnHeaderName: [
|
||||
'',
|
||||
[Validators.required, this._autocompleteStringValidator()]
|
||||
],
|
||||
delimiter: [undefined, 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.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.activeFields = [];
|
||||
|
||||
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) {
|
||||
entity.id = existing.id;
|
||||
entity.name = existing.label;
|
||||
@ -104,18 +117,35 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
}
|
||||
}
|
||||
|
||||
this.filteredKeyOptions = this.baseConfigForm.get('filenameMappingColumnHeaderName').valueChanges.pipe(
|
||||
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)
|
||||
)
|
||||
);
|
||||
this.filteredKeyOptions = this.baseConfigForm
|
||||
.get('filenameMappingColumnHeaderName')
|
||||
.valueChanges.pipe(
|
||||
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 (
|
||||
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 = {
|
||||
@ -169,14 +199,17 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
const newPrimary = !!this.activeFields.find((attr) => attr.primaryAttribute);
|
||||
|
||||
if (newPrimary) {
|
||||
this.data.existingConfiguration.fileAttributeConfigs.forEach((attr) => (attr.primaryAttribute = false));
|
||||
this.data.existingConfiguration.fileAttributeConfigs.forEach(
|
||||
(attr) => (attr.primaryAttribute = false)
|
||||
);
|
||||
}
|
||||
|
||||
const fileAttributes = {
|
||||
...this.baseConfigForm.getRawValue(),
|
||||
fileAttributeConfigs: [
|
||||
...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) => ({
|
||||
id: field.id,
|
||||
@ -190,9 +223,13 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
};
|
||||
|
||||
try {
|
||||
await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.ruleSetId).toPromise();
|
||||
await this._fileAttributesControllerService
|
||||
.setFileAttributesConfig(fileAttributes, this.ruleSetId)
|
||||
.toPromise();
|
||||
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,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
@ -217,7 +254,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
if (!column) {
|
||||
this.columnSample = [];
|
||||
} 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);
|
||||
}
|
||||
@ -238,7 +277,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent
|
||||
csvColumn,
|
||||
name: csvColumn,
|
||||
temporaryName: csvColumn,
|
||||
type: isNumber ? FileAttributeConfig.TypeEnum.NUMBER : FileAttributeConfig.TypeEnum.TEXT,
|
||||
type: isNumber
|
||||
? FileAttributeConfig.TypeEnum.NUMBER
|
||||
: FileAttributeConfig.TypeEnum.TEXT,
|
||||
readonly: false,
|
||||
primaryAttribute: false
|
||||
};
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group required w-300">
|
||||
<label translate="smtp-auth-config.form.username"></label>
|
||||
<input formControlName="user" name="user" 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 class="red-input-group required w-300">
|
||||
@ -18,9 +23,17 @@
|
||||
<button [disabled]="authForm.invalid" color="primary" mat-flat-button type="submit">
|
||||
{{ 'smtp-auth-config.actions.save' | translate }}
|
||||
</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>
|
||||
</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>
|
||||
|
||||
@ -22,7 +22,10 @@
|
||||
<div class="content-container">
|
||||
<div class="header-item">
|
||||
<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>
|
||||
<div class="actions-wrapper">
|
||||
<redaction-pagination
|
||||
@ -34,7 +37,10 @@
|
||||
<div class="red-input-group w-150 mr-20">
|
||||
<mat-form-field class="no-label">
|
||||
<mat-select formControlName="category">
|
||||
<mat-option *ngFor="let category of categories" [value]="category">
|
||||
<mat-option
|
||||
*ngFor="let category of categories"
|
||||
[value]="category"
|
||||
>
|
||||
{{ category | translate }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
@ -50,7 +56,10 @@
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></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-option *ngFor="let userId of userIds" [value]="userId">
|
||||
<redaction-initials-avatar
|
||||
@ -59,16 +68,26 @@
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
<div
|
||||
*ngIf="userId === ALL_USERS"
|
||||
[translate]="ALL_USERS"
|
||||
></div>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="separator">·</div>
|
||||
<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-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
<mat-icon
|
||||
matDatepickerToggleIcon
|
||||
svgIcon="red:calendar"
|
||||
></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #fromPicker></mat-datepicker>
|
||||
</div>
|
||||
@ -76,9 +95,16 @@
|
||||
<div class="mr-20" translate="audit-screen.to"></div>
|
||||
|
||||
<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-icon matDatepickerToggleIcon svgIcon="red:calendar"></mat-icon>
|
||||
<mat-icon
|
||||
matDatepickerToggleIcon
|
||||
svgIcon="red:calendar"
|
||||
></mat-icon>
|
||||
</mat-datepicker-toggle>
|
||||
<mat-datepicker #toPicker></mat-datepicker>
|
||||
</div>
|
||||
@ -87,14 +113,31 @@
|
||||
</div>
|
||||
|
||||
<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 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>
|
||||
<redaction-table-col-name
|
||||
column="message"
|
||||
label="audit-screen.table-col-names.message"
|
||||
></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>
|
||||
|
||||
<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>
|
||||
<div *cdkVirtualFor="let log of logs?.data" class="table-item">
|
||||
@ -105,7 +148,11 @@
|
||||
{{ log.recordDate | date: 'd MMM. yyyy, hh:mm a' }}
|
||||
</div>
|
||||
<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>
|
||||
{{ log.category }}
|
||||
@ -118,4 +165,6 @@
|
||||
</div>
|
||||
</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 {
|
||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.filterForm, 'from', 'to')) {
|
||||
if (
|
||||
applyIntervalConstraints(
|
||||
value,
|
||||
this._previousFrom,
|
||||
this._previousTo,
|
||||
this.filterForm,
|
||||
'from',
|
||||
'to'
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
<div class="flex-1 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>
|
||||
|
||||
@ -17,7 +22,10 @@
|
||||
<div class="content-container">
|
||||
<div class="header-item">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
@ -30,7 +38,10 @@
|
||||
label="default-colors-screen.table-col-names.key"
|
||||
></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 class="scrollbar-placeholder"></div>
|
||||
@ -38,13 +49,24 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<!-- 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 [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 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 class="actions-container">
|
||||
@ -66,4 +88,6 @@
|
||||
</div>
|
||||
</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',
|
||||
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;
|
||||
protected readonly _sortKey = 'default-colors';
|
||||
private _colorsObj: Colors;
|
||||
@ -35,9 +38,14 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{ key: st
|
||||
|
||||
openEditColorDialog($event: any, color: { key: string; value: string }) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openEditColorsDialog(this._colorsObj, color.key, this._appStateService.activeRuleSetId, async () => {
|
||||
this._loadColors();
|
||||
});
|
||||
this._dialogService.openEditColorsDialog(
|
||||
this._colorsObj,
|
||||
color.key,
|
||||
this._appStateService.activeRuleSetId,
|
||||
async () => {
|
||||
this._loadColors();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadColors() {
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
<div class="flex-1 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>
|
||||
|
||||
@ -25,11 +30,17 @@
|
||||
</div>
|
||||
|
||||
<span class="all-caps-label">
|
||||
{{ 'dictionary-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
||||
{{
|
||||
'dictionary-listing.table-header.title'
|
||||
| translate: { length: displayedEntities.length }
|
||||
}}
|
||||
</span>
|
||||
|
||||
<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">
|
||||
<redaction-icon-button
|
||||
(action)="openAddEditDictionaryDialog()"
|
||||
@ -42,7 +53,11 @@
|
||||
</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>
|
||||
|
||||
<redaction-table-col-name
|
||||
@ -62,7 +77,10 @@
|
||||
label="dictionary-listing.table-col-names.order-of-importance"
|
||||
></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 class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
@ -75,20 +93,32 @@
|
||||
screen="dictionary-listing"
|
||||
></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>
|
||||
<div
|
||||
*cdkVirtualFor="let dict of displayedEntities | sortBy: sortingOption.order:sortingOption.column"
|
||||
*cdkVirtualFor="
|
||||
let dict of displayedEntities
|
||||
| sortBy: sortingOption.order:sortingOption.column
|
||||
"
|
||||
[routerLink]="[dict.type]"
|
||||
class="table-item pointer"
|
||||
>
|
||||
<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 [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="table-item-title heading">
|
||||
{{ dict.label }}
|
||||
@ -111,7 +141,10 @@
|
||||
</div>
|
||||
|
||||
<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 class="actions-container">
|
||||
@ -154,4 +187,6 @@
|
||||
</div>
|
||||
</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',
|
||||
styleUrls: ['./dictionary-listing-screen.component.scss']
|
||||
})
|
||||
export class DictionaryListingScreenComponent extends BaseListingComponent<TypeValue> implements OnInit {
|
||||
export class DictionaryListingScreenComponent
|
||||
extends BaseListingComponent<TypeValue>
|
||||
implements OnInit
|
||||
{
|
||||
viewReady = false;
|
||||
chartData: DoughnutChartConfig[] = [];
|
||||
protected readonly _searchKey = 'label';
|
||||
@ -39,38 +42,50 @@ export class DictionaryListingScreenComponent extends BaseListingComponent<TypeV
|
||||
|
||||
openAddEditDictionaryDialog($event?: MouseEvent, dict?: TypeValue) {
|
||||
$event?.stopPropagation();
|
||||
this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
|
||||
if (newDictionary) {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
this._dialogService.openAddEditDictionaryDialog(
|
||||
dict,
|
||||
this._appStateService.activeRuleSetId,
|
||||
async (newDictionary) => {
|
||||
if (newDictionary) {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
openDeleteDictionaryDialog($event: any, dict: TypeValue) {
|
||||
this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
});
|
||||
this._dialogService.openDeleteDictionaryDialog(
|
||||
$event,
|
||||
dict,
|
||||
this._appStateService.activeRuleSetId,
|
||||
async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._loadDictionaryData();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadDictionaryData() {
|
||||
const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
|
||||
const appStateDictionaryData =
|
||||
this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
|
||||
this.allEntities = Object.keys(appStateDictionaryData)
|
||||
.map((key) => appStateDictionaryData[key])
|
||||
.filter((d) => !d.virtual);
|
||||
this.displayedEntities = [...this.allEntities];
|
||||
const dataObs = this.allEntities.map((dict) =>
|
||||
this._dictionaryControllerService.getDictionaryForType(this._appStateService.activeRuleSetId, dict.type).pipe(
|
||||
tap((values) => {
|
||||
dict.entries = values.entries ? values.entries : [];
|
||||
}),
|
||||
catchError(() => {
|
||||
console.log('error');
|
||||
dict.entries = [];
|
||||
return of({});
|
||||
})
|
||||
)
|
||||
this._dictionaryControllerService
|
||||
.getDictionaryForType(this._appStateService.activeRuleSetId, dict.type)
|
||||
.pipe(
|
||||
tap((values) => {
|
||||
dict.entries = values.entries ? values.entries : [];
|
||||
}),
|
||||
catchError(() => {
|
||||
console.log('error');
|
||||
dict.entries = [];
|
||||
return of({});
|
||||
})
|
||||
)
|
||||
);
|
||||
forkJoin(dataObs)
|
||||
.pipe(defaultIfEmpty(null))
|
||||
|
||||
@ -38,7 +38,14 @@
|
||||
tooltipPosition="below"
|
||||
></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
|
||||
[routerLink]="['..']"
|
||||
@ -92,11 +99,16 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</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;
|
||||
entries: string[] = [];
|
||||
|
||||
@ViewChild('dictionaryManager', { static: false }) private _dictionaryManager: DictionaryManagerComponent;
|
||||
@ViewChild('dictionaryManager', { static: false })
|
||||
private _dictionaryManager: DictionaryManagerComponent;
|
||||
@ViewChild('fileInput') private _fileInput: ElementRef;
|
||||
|
||||
constructor(
|
||||
@ -37,7 +38,10 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
private readonly _formBuilder: FormBuilder
|
||||
) {
|
||||
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 {
|
||||
@ -54,16 +58,25 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
|
||||
openEditDictionaryDialog($event: any) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openAddEditDictionaryDialog(this.dictionary, this.dictionary.ruleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
});
|
||||
this._dialogService.openAddEditDictionaryDialog(
|
||||
this.dictionary,
|
||||
this.dictionary.ruleSetId,
|
||||
async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
openDeleteDictionaryDialog($event: any) {
|
||||
this._dialogService.openDeleteDictionaryDialog($event, this.dictionary, this.dictionary.ruleSetId, async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._router.navigate(['..']);
|
||||
});
|
||||
this._dialogService.openDeleteDictionaryDialog(
|
||||
$event,
|
||||
this.dictionary,
|
||||
this.dictionary.ruleSetId,
|
||||
async () => {
|
||||
await this._appStateService.loadDictionaryData();
|
||||
this._router.navigate(['..']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
download(): void {
|
||||
@ -89,28 +102,40 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
||||
|
||||
saveEntries(entries: string[]) {
|
||||
this.processing = true;
|
||||
this._dictionarySaveService.saveEntries(entries, this.entries, this.dictionary.ruleSetId, this.dictionary.type, null).subscribe(
|
||||
() => {
|
||||
this.processing = false;
|
||||
this._loadEntries();
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
this._dictionarySaveService
|
||||
.saveEntries(
|
||||
entries,
|
||||
this.entries,
|
||||
this.dictionary.ruleSetId,
|
||||
this.dictionary.type,
|
||||
null
|
||||
)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.processing = false;
|
||||
this._loadEntries();
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadEntries() {
|
||||
this.processing = true;
|
||||
this._dictionaryControllerService.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type).subscribe(
|
||||
(data) => {
|
||||
this.processing = false;
|
||||
this.entries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
this.entries = [];
|
||||
}
|
||||
);
|
||||
this._dictionaryControllerService
|
||||
.getDictionaryForType(this.dictionary.ruleSetId, this.dictionary.type)
|
||||
.subscribe(
|
||||
(data) => {
|
||||
this.processing = false;
|
||||
this.entries = data.entries.sort((str1, str2) =>
|
||||
str1.localeCompare(str2, undefined, { sensitivity: 'accent' })
|
||||
);
|
||||
},
|
||||
() => {
|
||||
this.processing = false;
|
||||
this.entries = [];
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,8 +22,19 @@
|
||||
<div class="red-content-inner">
|
||||
<div class="content-container">
|
||||
<div class="content-container-content">
|
||||
<form (keyup)="formChanged()" *ngIf="digitalSignatureForm" [formGroup]="digitalSignatureForm" autocomplete="off">
|
||||
<input #fileInput (change)="fileChanged($event, fileInput)" class="file-upload-input" hidden type="file" />
|
||||
<form
|
||||
(keyup)="formChanged()"
|
||||
*ngIf="digitalSignatureForm"
|
||||
[formGroup]="digitalSignatureForm"
|
||||
autocomplete="off"
|
||||
>
|
||||
<input
|
||||
#fileInput
|
||||
(change)="fileChanged($event, fileInput)"
|
||||
class="file-upload-input"
|
||||
hidden
|
||||
type="file"
|
||||
/>
|
||||
|
||||
<redaction-empty-state
|
||||
(action)="fileInput.click()"
|
||||
@ -32,34 +43,66 @@
|
||||
screen="digital-signature-screen"
|
||||
></redaction-empty-state>
|
||||
|
||||
<div [class.hidden]="!hasDigitalSignatureSet" class="red-input-group required w-300">
|
||||
<label translate="digital-signature-screen.certificate-name.label"></label>
|
||||
<div
|
||||
[class.hidden]="!hasDigitalSignatureSet"
|
||||
class="red-input-group required w-300"
|
||||
>
|
||||
<label
|
||||
translate="digital-signature-screen.certificate-name.label"
|
||||
></label>
|
||||
<input
|
||||
[placeholder]="'digital-signature-screen.certificate-name.placeholder' | translate"
|
||||
[placeholder]="
|
||||
'digital-signature-screen.certificate-name.placeholder'
|
||||
| translate
|
||||
"
|
||||
formControlName="certificateName"
|
||||
name="certificateName"
|
||||
/>
|
||||
</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>
|
||||
<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 [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||
<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 [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||
<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 [class.hidden]="!hasDigitalSignatureSet" class="red-input-group w-300">
|
||||
<label translate="digital-signature-screen.contact-info.label"></label>
|
||||
<input
|
||||
[placeholder]="'digital-signature-screen.contact-info.placeholder' | translate"
|
||||
[placeholder]="
|
||||
'digital-signature-screen.contact-info.placeholder' | translate
|
||||
"
|
||||
formControlName="contactInfo"
|
||||
name="contactInfo"
|
||||
/>
|
||||
@ -96,4 +139,6 @@
|
||||
</div>
|
||||
</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() {
|
||||
return this.digitalSignatureExists || !!this.digitalSignatureForm.get('base64EncodedPrivateKey').value;
|
||||
return (
|
||||
this.digitalSignatureExists ||
|
||||
!!this.digitalSignatureForm.get('base64EncodedPrivateKey').value
|
||||
);
|
||||
}
|
||||
|
||||
saveDigitalSignature() {
|
||||
@ -55,13 +58,17 @@ export class DigitalSignatureScreenComponent {
|
||||
(error) => {
|
||||
if (error.status === 400) {
|
||||
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,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
} else {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('digital-signature-screen.action.save-error'),
|
||||
this._translateService.instant(
|
||||
'digital-signature-screen.action.save-error'
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
@ -75,7 +82,9 @@ export class DigitalSignatureScreenComponent {
|
||||
() => {
|
||||
this.loadDigitalSignatureAndInitializeForm();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('digital-signature-screen.action.delete-success'),
|
||||
this._translateService.instant(
|
||||
'digital-signature-screen.action.delete-success'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
@ -130,9 +139,13 @@ export class DigitalSignatureScreenComponent {
|
||||
certificateName: [this.digitalSignature.certificateName, Validators.required],
|
||||
contactInfo: this.digitalSignature.contactInfo,
|
||||
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,
|
||||
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>
|
||||
|
||||
<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>
|
||||
|
||||
@ -23,7 +28,10 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<redaction-circle-button
|
||||
@ -38,8 +46,17 @@
|
||||
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
|
||||
|
||||
<div class="attributes-actions-container">
|
||||
<redaction-search-input [form]="searchForm" [placeholder]="'file-attributes-listing.search'"></redaction-search-input>
|
||||
<input #fileInput (change)="importCSV($event.target['files'])" accept=".csv" class="csv-input" type="file" />
|
||||
<redaction-search-input
|
||||
[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
|
||||
(action)="fileInput.click()"
|
||||
@ -58,7 +75,11 @@
|
||||
</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>
|
||||
|
||||
<redaction-table-col-name
|
||||
@ -86,7 +107,9 @@
|
||||
label="file-attributes-listing.table-col-names.read-only"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name label="file-attributes-listing.table-col-names.csv-column"></redaction-table-col-name>
|
||||
<redaction-table-col-name
|
||||
label="file-attributes-listing.table-col-names.csv-column"
|
||||
></redaction-table-col-name>
|
||||
|
||||
<redaction-table-col-name
|
||||
class="flex-center"
|
||||
@ -100,7 +123,11 @@
|
||||
<div class="scrollbar-placeholder"></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
|
||||
*ngIf="allEntities.length && !displayedEntities.length"
|
||||
@ -110,16 +137,27 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="80" redactionHasScrollbar>
|
||||
<!-- 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">
|
||||
<redaction-round-checkbox [active]="isEntitySelected(attribute)"></redaction-round-checkbox>
|
||||
<redaction-round-checkbox
|
||||
[active]="isEntitySelected(attribute)"
|
||||
></redaction-round-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="label">
|
||||
<span>{{ attribute.label }}</span>
|
||||
</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">
|
||||
<mat-icon
|
||||
@ -133,7 +171,11 @@
|
||||
{{ attribute.csvColumnHeader }}
|
||||
</div>
|
||||
<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 class="actions-container">
|
||||
<div class="action-buttons">
|
||||
@ -162,4 +204,6 @@
|
||||
</div>
|
||||
</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 { 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 { ActivatedRoute } from '@angular/router';
|
||||
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',
|
||||
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;
|
||||
loading = false;
|
||||
protected readonly _searchKey = 'label';
|
||||
@ -39,38 +46,65 @@ export class FileAttributesListingScreenComponent extends BaseListingComponent<F
|
||||
|
||||
openAddEditAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openAddEditFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async (newValue: FileAttributeConfig) => {
|
||||
this.loading = true;
|
||||
await this._fileAttributesService.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId).toPromise();
|
||||
await this._loadData();
|
||||
});
|
||||
this._dialogService.openAddEditFileAttributeDialog(
|
||||
fileAttribute,
|
||||
this._appStateService.activeRuleSetId,
|
||||
async (newValue: FileAttributeConfig) => {
|
||||
this.loading = true;
|
||||
await this._fileAttributesService
|
||||
.setFileAttributesConfiguration(newValue, this._appStateService.activeRuleSetId)
|
||||
.toPromise();
|
||||
await this._loadData();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
openConfirmDeleteAttributeDialog($event: MouseEvent, fileAttribute?: FileAttributeConfig) {
|
||||
$event.stopPropagation();
|
||||
this._dialogService.openConfirmDeleteFileAttributeDialog(fileAttribute, this._appStateService.activeRuleSetId, async () => {
|
||||
this.loading = true;
|
||||
if (fileAttribute) {
|
||||
await this._fileAttributesService.deleteFileAttribute(this._appStateService.activeRuleSetId, fileAttribute.id).toPromise();
|
||||
} else {
|
||||
await this._fileAttributesService.deleteFileAttributes(this.selectedEntitiesIds, this._appStateService.activeRuleSetId).toPromise();
|
||||
this._dialogService.openConfirmDeleteFileAttributeDialog(
|
||||
fileAttribute,
|
||||
this._appStateService.activeRuleSetId,
|
||||
async () => {
|
||||
this.loading = true;
|
||||
if (fileAttribute) {
|
||||
await this._fileAttributesService
|
||||
.deleteFileAttribute(
|
||||
this._appStateService.activeRuleSetId,
|
||||
fileAttribute.id
|
||||
)
|
||||
.toPromise();
|
||||
} else {
|
||||
await this._fileAttributesService
|
||||
.deleteFileAttributes(
|
||||
this.selectedEntitiesIds,
|
||||
this._appStateService.activeRuleSetId
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
await this._loadData();
|
||||
}
|
||||
await this._loadData();
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
importCSV(files: FileList | File[]) {
|
||||
const csvFile = files[0];
|
||||
this._fileInput.nativeElement.value = null;
|
||||
|
||||
this._dialogService.openImportFileAttributeCSVDialog(csvFile, this._appStateService.activeRuleSetId, this._existingConfiguration, async () => {
|
||||
await this._loadData();
|
||||
});
|
||||
this._dialogService.openImportFileAttributeCSVDialog(
|
||||
csvFile,
|
||||
this._appStateService.activeRuleSetId,
|
||||
this._existingConfiguration,
|
||||
async () => {
|
||||
await this._loadData();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
const response = await this._fileAttributesService.getFileAttributesConfiguration(this._appStateService.activeRuleSetId).toPromise();
|
||||
const response = await this._fileAttributesService
|
||||
.getFileAttributesConfiguration(this._appStateService.activeRuleSetId)
|
||||
.toPromise();
|
||||
this._existingConfiguration = response;
|
||||
this.allEntities = response?.fileAttributeConfigs || [];
|
||||
} catch (e) {
|
||||
|
||||
@ -8,7 +8,12 @@
|
||||
<div class="breadcrumb" translate="license-information"></div>
|
||||
|
||||
<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
|
||||
*ngIf="permissionsService.isUser()"
|
||||
class="ml-6"
|
||||
@ -40,7 +45,12 @@
|
||||
|
||||
<div class="row">
|
||||
<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 class="row">
|
||||
@ -54,7 +64,10 @@
|
||||
<!-- <div>Future feature: we will provide that with the /info endpoint</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 translate="license-info-screen.licensed-to"></div>
|
||||
@ -63,7 +76,10 @@
|
||||
|
||||
<div class="row">
|
||||
<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 class="row">
|
||||
@ -76,16 +92,28 @@
|
||||
<div>{{ currentInfo.numberOfAnalyzedPages }}</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>{{ '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>
|
||||
|
||||
<div class="row">
|
||||
<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 *ngIf="!!unlicensedInfo" class="row">
|
||||
@ -123,4 +151,6 @@
|
||||
</div>
|
||||
</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(),
|
||||
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())) {
|
||||
const unlicensedConfig = {
|
||||
startDate: endDate.toDate()
|
||||
};
|
||||
promises.push(this._licenseReportController.licenseReport(unlicensedConfig).toPromise());
|
||||
promises.push(
|
||||
this._licenseReportController.licenseReport(unlicensedConfig).toPromise()
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(promises).then((reports) => {
|
||||
[this.currentInfo, this.totalInfo, this.unlicensedInfo] = reports;
|
||||
this.viewReady = true;
|
||||
this.analysisPercentageOfLicense =
|
||||
this.totalLicensedNumberOfPages > 0 ? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) * 100 : 100;
|
||||
this.totalLicensedNumberOfPages > 0
|
||||
? (this.currentInfo.numberOfAnalyzedPages / this.totalLicensedNumberOfPages) *
|
||||
100
|
||||
: 100;
|
||||
});
|
||||
}
|
||||
|
||||
sendMail(): void {
|
||||
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 = [
|
||||
this._translateService.instant('license-info-screen.email.body.analyzed', { pages: this.currentInfo.numberOfAnalyzedPages }),
|
||||
this._translateService.instant('license-info-screen.email.body.licensed', { pages: this.totalLicensedNumberOfPages })
|
||||
this._translateService.instant('license-info-screen.email.body.analyzed', {
|
||||
pages: this.currentInfo.numberOfAnalyzedPages
|
||||
}),
|
||||
this._translateService.instant('license-info-screen.email.body.licensed', {
|
||||
pages: this.totalLicensedNumberOfPages
|
||||
})
|
||||
].join('%0D%0A');
|
||||
window.location.href = `mailto:${this.appConfigService.getConfig('LICENSE_EMAIL')}?subject=${subject}&body=${body}`;
|
||||
window.location.href = `mailto:${this.appConfigService.getConfig(
|
||||
'LICENSE_EMAIL'
|
||||
)}?subject=${subject}&body=${body}`;
|
||||
}
|
||||
|
||||
private async _setMonthlyStats(startDate: moment.Moment, endDate: moment.Moment) {
|
||||
|
||||
@ -28,15 +28,24 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
|
||||
<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
|
||||
(action)="openAddRuleSetDialog()"
|
||||
*ngIf="permissionsService.isAdmin() && userPreferenceService.areDevFeaturesEnabled"
|
||||
*ngIf="
|
||||
permissionsService.isAdmin() &&
|
||||
userPreferenceService.areDevFeaturesEnabled
|
||||
"
|
||||
icon="red:plus"
|
||||
text="project-templates-listing.add-new"
|
||||
type="primary"
|
||||
@ -44,7 +53,11 @@
|
||||
</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>
|
||||
|
||||
<redaction-table-col-name
|
||||
@ -54,7 +67,10 @@
|
||||
column="name"
|
||||
label="project-templates-listing.table-col-names.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
|
||||
(toggleSort)="toggleSort($event)"
|
||||
[activeSortingOption]="sortingOption"
|
||||
@ -72,7 +88,11 @@
|
||||
<div class="scrollbar-placeholder"></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
|
||||
*ngIf="allEntities.length && !displayedEntities.length"
|
||||
@ -82,12 +102,20 @@
|
||||
|
||||
<cdk-virtual-scroll-viewport [itemSize]="100" redactionHasScrollbar>
|
||||
<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']"
|
||||
class="table-item pointer"
|
||||
>
|
||||
<div (click)="toggleEntitySelected($event, ruleSet)" class="selection-column">
|
||||
<redaction-round-checkbox [active]="isEntitySelected(ruleSet)"></redaction-round-checkbox>
|
||||
<div
|
||||
(click)="toggleEntitySelected($event, ruleSet)"
|
||||
class="selection-column"
|
||||
>
|
||||
<redaction-round-checkbox
|
||||
[active]="isEntitySelected(ruleSet)"
|
||||
></redaction-round-checkbox>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -97,13 +125,19 @@
|
||||
<div class="small-label stats-subtitle">
|
||||
<div>
|
||||
<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 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 class="small-label">
|
||||
{{ ruleSet.dateAdded | date: 'd MMM. yyyy' }}
|
||||
|
||||
@ -11,7 +11,10 @@ import { RuleSetModel } from '@redaction/red-ui-http';
|
||||
templateUrl: './rule-sets-listing-screen.component.html',
|
||||
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 _selectionKey = 'ruleSetId';
|
||||
protected readonly _sortKey = 'rule-sets-listing';
|
||||
|
||||
@ -5,7 +5,12 @@
|
||||
<div class="flex-1 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>
|
||||
|
||||
@ -29,10 +34,21 @@
|
||||
</ace-editor>
|
||||
</div>
|
||||
<div *ngIf="hasChanges && permissionsService.isAdmin()" class="changes-box">
|
||||
<redaction-icon-button (action)="save()" icon="red:check" text="rules-screen.save-changes" type="primary"></redaction-icon-button>
|
||||
<div (click)="revert()" class="all-caps-label cancel" translate="rules-screen.revert-changes"></div>
|
||||
<redaction-icon-button
|
||||
(action)="save()"
|
||||
icon="red:check"
|
||||
text="rules-screen.save-changes"
|
||||
type="primary"
|
||||
></redaction-icon-button>
|
||||
<div
|
||||
(click)="revert()"
|
||||
class="all-caps-label cancel"
|
||||
translate="rules-screen.revert-changes"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<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];
|
||||
if (entry?.trim().length > 0) {
|
||||
// only mark non-empty lines
|
||||
this.activeEditMarkers.push(this.editorComponent.getEditor().getSession().addMarker(new range(i, 0, i, 1), 'changed-row-marker', 'fullLine'));
|
||||
this.activeEditMarkers.push(
|
||||
this.editorComponent
|
||||
.getEditor()
|
||||
.getSession()
|
||||
.addMarker(new range(i, 0, i, 1), 'changed-row-marker', 'fullLine')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,7 +97,11 @@ export class RulesScreenComponent extends ComponentHasChanges {
|
||||
},
|
||||
() => {
|
||||
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
|
||||
formControlName="host"
|
||||
name="host"
|
||||
placeholder="{{ 'smtp-config-screen.form.host-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'smtp-config-screen.form.host-placeholder' | translate
|
||||
}}"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
@ -44,7 +46,9 @@
|
||||
<input
|
||||
formControlName="port"
|
||||
name="port"
|
||||
placeholder="{{ 'smtp-config-screen.form.port-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'smtp-config-screen.form.port-placeholder' | translate
|
||||
}}"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
@ -54,20 +58,30 @@
|
||||
<input
|
||||
formControlName="from"
|
||||
name="from"
|
||||
placeholder="{{ 'smtp-config-screen.form.from-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'smtp-config-screen.form.from-placeholder' | translate
|
||||
}}"
|
||||
type="email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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
|
||||
formControlName="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"
|
||||
/>
|
||||
<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 class="red-input-group">
|
||||
@ -75,44 +89,69 @@
|
||||
<input
|
||||
formControlName="replyTo"
|
||||
name="replyTo"
|
||||
placeholder="{{ 'smtp-config-screen.form.reply-to-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'smtp-config-screen.form.reply-to-placeholder'
|
||||
| translate
|
||||
}}"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<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
|
||||
formControlName="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"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<label translate="smtp-config-screen.form.envelope-from"></label>
|
||||
<label
|
||||
translate="smtp-config-screen.form.envelope-from"
|
||||
></label>
|
||||
<input
|
||||
formControlName="envelopeFrom"
|
||||
name="envelopeFrom"
|
||||
placeholder="{{ 'smtp-config-screen.form.envelope-from-placeholder' | translate }}"
|
||||
placeholder="{{
|
||||
'smtp-config-screen.form.envelope-from-placeholder'
|
||||
| translate
|
||||
}}"
|
||||
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 class="red-input-group">
|
||||
<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 class="red-input-group">
|
||||
<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 class="red-input-group">
|
||||
<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
|
||||
(click)="openAuthConfigDialog(true)"
|
||||
@ -123,7 +162,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<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 }}
|
||||
</button>
|
||||
|
||||
@ -141,4 +185,6 @@
|
||||
</div>
|
||||
</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() {
|
||||
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.viewReady = true;
|
||||
}
|
||||
|
||||
openAuthConfigDialog(skipDisableOnCancel?: boolean) {
|
||||
this._dialogService.openSMTPAuthConfigDialog(this.configForm.getRawValue(), (authConfig) => {
|
||||
if (authConfig) {
|
||||
this.configForm.patchValue(authConfig);
|
||||
} else if (!skipDisableOnCancel) {
|
||||
this.configForm.patchValue({ auth: false }, { emitEvent: false });
|
||||
this._dialogService.openSMTPAuthConfigDialog(
|
||||
this.configForm.getRawValue(),
|
||||
(authConfig) => {
|
||||
if (authConfig) {
|
||||
this.configForm.patchValue(authConfig);
|
||||
} else if (!skipDisableOnCancel) {
|
||||
this.configForm.patchValue({ auth: false }, { emitEvent: false });
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
|
||||
async testConnection() {
|
||||
this.viewReady = false;
|
||||
try {
|
||||
await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise();
|
||||
await this._smtpConfigService
|
||||
.testSMTPConfiguration(this.configForm.getRawValue())
|
||||
.toPromise();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant('smtp-config-screen.test.success'),
|
||||
undefined,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
} 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 {
|
||||
this.viewReady = true;
|
||||
}
|
||||
@ -98,7 +109,9 @@ export class SmtpConfigScreenComponent implements OnInit {
|
||||
|
||||
private async _loadData() {
|
||||
try {
|
||||
this._initialValue = await this._smtpConfigService.getCurrentSMTPConfiguration().toPromise();
|
||||
this._initialValue = await this._smtpConfigService
|
||||
.getCurrentSMTPConfiguration()
|
||||
.toPromise();
|
||||
this.configForm.patchValue(this._initialValue, { emitEvent: false });
|
||||
} catch (e) {
|
||||
} finally {
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
<div class="breadcrumb" translate="user-management"></div>
|
||||
|
||||
<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
|
||||
(action)="openAddEditUserDialog($event)"
|
||||
*ngIf="permissionsService.isUserAdmin()"
|
||||
@ -39,14 +42,21 @@
|
||||
</div>
|
||||
|
||||
<span class="all-caps-label">
|
||||
{{ 'user-listing.table-header.title' | translate: { length: displayedEntities.length } }}
|
||||
{{
|
||||
'user-listing.table-header.title'
|
||||
| translate: { length: displayedEntities.length }
|
||||
}}
|
||||
</span>
|
||||
|
||||
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
|
||||
<redaction-circle-button
|
||||
(action)="bulkDelete()"
|
||||
[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"
|
||||
tooltipPosition="after"
|
||||
type="dark-bg"
|
||||
@ -59,33 +69,56 @@
|
||||
<div class="table-header" redactionSyncWidth="table-item">
|
||||
<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 class="scrollbar-placeholder"></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>
|
||||
<!-- Table lines -->
|
||||
<div *cdkVirtualFor="let user of displayedEntities" class="table-item">
|
||||
<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>
|
||||
<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 class="small-label">{{ user.email || '-' }}</div>
|
||||
<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 class="small-label">{{ getDisplayRoles(user) }}</div>
|
||||
<div class="actions-container">
|
||||
@ -113,10 +146,15 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</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') {
|
||||
await this._userControllerService.createUser(result.user).toPromise();
|
||||
} 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();
|
||||
}
|
||||
@ -67,7 +69,10 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
}
|
||||
|
||||
getDisplayRoles(user: User) {
|
||||
return user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') || this._translateService.instant('roles.NO_ROLE');
|
||||
return (
|
||||
user.roles.map((role) => this._translateService.instant('roles.' + role)).join(', ') ||
|
||||
this._translateService.instant('roles.NO_ROLE')
|
||||
);
|
||||
}
|
||||
|
||||
async toggleActive(user: User) {
|
||||
@ -101,32 +106,46 @@ export class UserListingScreenComponent extends BaseListingComponent<User> imple
|
||||
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',
|
||||
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',
|
||||
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',
|
||||
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',
|
||||
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',
|
||||
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',
|
||||
label: 'RED_ADMIN'
|
||||
}
|
||||
|
||||
@ -3,7 +3,12 @@
|
||||
<redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
|
||||
|
||||
<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>
|
||||
|
||||
@ -22,7 +27,11 @@
|
||||
text="watermark-screen.action.save"
|
||||
type="primary"
|
||||
></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>
|
||||
|
||||
@ -43,7 +52,10 @@
|
||||
</div>
|
||||
|
||||
<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
|
||||
(click)="setValue('orientation', option)"
|
||||
@ -58,17 +70,34 @@
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<label 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>
|
||||
<label
|
||||
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 class="red-input-group">
|
||||
<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 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
|
||||
class="hex-color-input"
|
||||
formControlName="hexColor"
|
||||
@ -88,14 +117,20 @@
|
||||
class="input-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"
|
||||
></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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
|
||||
(click)="setValue('fontType', option.value)"
|
||||
@ -119,4 +154,6 @@
|
||||
</div>
|
||||
</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
|
||||
? this._watermarkControllerService.saveWatermark(watermark, this.appStateService.activeRuleSetId)
|
||||
: this._watermarkControllerService.deleteWatermark(this.appStateService.activeRuleSetId);
|
||||
? this._watermarkControllerService.saveWatermark(
|
||||
watermark,
|
||||
this.appStateService.activeRuleSetId
|
||||
)
|
||||
: this._watermarkControllerService.deleteWatermark(
|
||||
this.appStateService.activeRuleSetId
|
||||
);
|
||||
|
||||
observable.subscribe(
|
||||
() => {
|
||||
this._loadWatermark();
|
||||
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,
|
||||
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() {
|
||||
this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe(
|
||||
(watermark) => {
|
||||
this._watermark = watermark;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
},
|
||||
() => {
|
||||
this._watermark = DEFAULT_WATERMARK;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
}
|
||||
);
|
||||
this._watermarkControllerService
|
||||
.getWatermark(this.appStateService.activeRuleSetId)
|
||||
.subscribe(
|
||||
(watermark) => {
|
||||
this._watermark = watermark;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
},
|
||||
() => {
|
||||
this._watermark = DEFAULT_WATERMARK;
|
||||
this.configForm.setValue({ ...this._watermark });
|
||||
this._loadViewer();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _loadViewer() {
|
||||
@ -177,14 +192,17 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
const text = this.configForm.get('text').value || '';
|
||||
const fontSize = this.configForm.get('fontSize').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 color = this.configForm.get('hexColor').value;
|
||||
|
||||
const rgbColor = hexToRgb(color);
|
||||
|
||||
const stamper = await pdfNet.Stamper.create(3, fontSize, 0);
|
||||
await stamper.setFontColor(await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255));
|
||||
await stamper.setFontColor(
|
||||
await pdfNet.ColorPt.init(rgbColor.r / 255, rgbColor.g / 255, rgbColor.b / 255)
|
||||
);
|
||||
await stamper.setOpacity(opacity / 100);
|
||||
|
||||
switch (orientation) {
|
||||
@ -200,7 +218,10 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
await stamper.setRotation(-45);
|
||||
}
|
||||
|
||||
const font = await pdfNet.Font.createAndEmbed(document, this._convertFont(fontType));
|
||||
const font = await pdfNet.Font.createAndEmbed(
|
||||
document,
|
||||
this._convertFont(fontType)
|
||||
);
|
||||
await stamper.setFont(font);
|
||||
await stamper.setTextAlignment(0);
|
||||
await stamper.stampText(document, text, pageSet);
|
||||
@ -216,11 +237,26 @@ export class WatermarkScreenComponent implements OnInit {
|
||||
private _initForm() {
|
||||
this.configForm = this._formBuilder.group({
|
||||
text: [{ value: null, disabled: !this.permissionsService.isAdmin() }],
|
||||
hexColor: [{ 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]
|
||||
hexColor: [
|
||||
{ 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
|
||||
) {}
|
||||
|
||||
openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
openDeleteDictionaryDialog(
|
||||
$event: MouseEvent,
|
||||
dictionary: TypeValue,
|
||||
ruleSetId: string,
|
||||
cb?: Function
|
||||
): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(async (result) => {
|
||||
if (result) {
|
||||
await this._dictionaryControllerService.deleteType(dictionary.type, ruleSetId).toPromise();
|
||||
await this._dictionaryControllerService
|
||||
.deleteType(dictionary.type, ruleSetId)
|
||||
.toPromise();
|
||||
if (cb) cb();
|
||||
}
|
||||
});
|
||||
return ref;
|
||||
}
|
||||
|
||||
openDeleteRuleSetDialog($event: MouseEvent, ruleSet: RuleSetModel, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
|
||||
openDeleteRuleSetDialog(
|
||||
$event: MouseEvent,
|
||||
ruleSet: RuleSetModel,
|
||||
cb?: Function
|
||||
): MatDialogRef<ConfirmationDialogComponent> {
|
||||
$event.stopPropagation();
|
||||
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
|
||||
ref.afterClosed().subscribe(async (result) => {
|
||||
@ -77,7 +88,11 @@ export class AdminDialogService {
|
||||
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, {
|
||||
...dialogConfig,
|
||||
data: { dictionary, ruleSetId },
|
||||
@ -93,7 +108,12 @@ export class AdminDialogService {
|
||||
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, {
|
||||
...dialogConfig,
|
||||
data: { colors, colorKey, ruleSetId },
|
||||
@ -109,7 +129,10 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
openAddEditRuleSetDialog(ruleSet: RuleSetModel, cb?: Function): MatDialogRef<AddEditRuleSetDialogComponent> {
|
||||
openAddEditRuleSetDialog(
|
||||
ruleSet: RuleSetModel,
|
||||
cb?: Function
|
||||
): MatDialogRef<AddEditRuleSetDialogComponent> {
|
||||
const ref = this._dialog.open(AddEditRuleSetDialogComponent, {
|
||||
...dialogConfig,
|
||||
width: '900px',
|
||||
@ -146,7 +169,11 @@ export class AdminDialogService {
|
||||
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, {
|
||||
...dialogConfig,
|
||||
data: { fileAttribute, ruleSetId },
|
||||
@ -182,7 +209,10 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
openSMTPAuthConfigDialog(smtpConfig: SMTPConfigurationModel, cb?: Function): MatDialogRef<SmtpAuthDialogComponent> {
|
||||
openSMTPAuthConfigDialog(
|
||||
smtpConfig: SMTPConfigurationModel,
|
||||
cb?: Function
|
||||
): MatDialogRef<SmtpAuthDialogComponent> {
|
||||
const ref = this._dialog.open(SmtpAuthDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: smtpConfig,
|
||||
@ -214,7 +244,10 @@ export class AdminDialogService {
|
||||
return ref;
|
||||
}
|
||||
|
||||
openConfirmDeleteUsersDialog(users: User[], cb?: Function): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
||||
openConfirmDeleteUsersDialog(
|
||||
users: User[],
|
||||
cb?: Function
|
||||
): MatDialogRef<ConfirmDeleteUsersDialogComponent> {
|
||||
const ref = this._dialog.open(ConfirmDeleteUsersDialogComponent, {
|
||||
...dialogConfig,
|
||||
data: users,
|
||||
|
||||
@ -34,17 +34,28 @@ export enum AppConfigKey {
|
||||
export class AppConfigService {
|
||||
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> {
|
||||
this._cacheApiService.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION).then(async (lastVersion) => {
|
||||
console.log('[REDACTION] Last app version: ', lastVersion, ' current version ', version);
|
||||
if (lastVersion !== version) {
|
||||
console.log('[REDACTION] Version-missmatch - wiping caches!');
|
||||
await wipeCaches();
|
||||
}
|
||||
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, version);
|
||||
});
|
||||
this._cacheApiService
|
||||
.getCachedValue(AppConfigKey.FRONTEND_APP_VERSION)
|
||||
.then(async (lastVersion) => {
|
||||
console.log(
|
||||
'[REDACTION] Last app version: ',
|
||||
lastVersion,
|
||||
' current version ',
|
||||
version
|
||||
);
|
||||
if (lastVersion !== version) {
|
||||
console.log('[REDACTION] Version-missmatch - wiping caches!');
|
||||
await wipeCaches();
|
||||
}
|
||||
await this._cacheApiService.cacheValue(AppConfigKey.FRONTEND_APP_VERSION, version);
|
||||
});
|
||||
|
||||
return this._httpClient.get<any>('/assets/config/config.json').pipe(
|
||||
tap((config) => {
|
||||
|
||||
@ -7,7 +7,11 @@ import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloa
|
||||
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
|
||||
import { BASE_HREF } from '../../tokens';
|
||||
|
||||
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService, baseUrl) {
|
||||
export function keycloakInitializer(
|
||||
keycloak: KeycloakService,
|
||||
appConfigService: AppConfigService,
|
||||
baseUrl
|
||||
) {
|
||||
return () =>
|
||||
appConfigService
|
||||
.loadAppConfig()
|
||||
@ -26,12 +30,15 @@ export function keycloakInitializer(keycloak: KeycloakService, appConfigService:
|
||||
initOptions: {
|
||||
checkLoginIframe: false,
|
||||
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'
|
||||
},
|
||||
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'
|
||||
})
|
||||
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> {
|
||||
return new Observable((obs) => {
|
||||
@ -20,7 +24,11 @@ export class RedRoleGuard implements CanActivate {
|
||||
} else {
|
||||
// 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']);
|
||||
obs.next(false);
|
||||
obs.complete();
|
||||
|
||||
@ -9,7 +9,10 @@ import { DomSanitizer } from '@angular/platform-browser';
|
||||
exports: [MatIconModule]
|
||||
})
|
||||
export class IconsModule {
|
||||
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
|
||||
constructor(
|
||||
private readonly _iconRegistry: MatIconRegistry,
|
||||
private readonly _sanitizer: DomSanitizer
|
||||
) {
|
||||
const icons = [
|
||||
'add',
|
||||
'analyse',
|
||||
@ -84,7 +87,11 @@ export class IconsModule {
|
||||
];
|
||||
|
||||
for (const icon of icons) {
|
||||
_iconRegistry.addSvgIconInNamespace('red', icon, _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${icon}.svg`));
|
||||
_iconRegistry.addSvgIconInNamespace(
|
||||
'red',
|
||||
icon,
|
||||
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${icon}.svg`)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,13 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.convertRecommendationToAnnotation($event, [annotation], annotationsChanged)"
|
||||
(action)="
|
||||
annotationActionsService.convertRecommendationToAnnotation(
|
||||
$event,
|
||||
[annotation],
|
||||
annotationsChanged
|
||||
)
|
||||
"
|
||||
*ngIf="annotationPermissions.canAcceptRecommendation"
|
||||
icon="red:check"
|
||||
tooltip="annotation-actions.accept-recommendation.label"
|
||||
@ -20,8 +26,13 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canMarkTextOnlyAsFalsePositive && !annotationPermissions.canPerformMultipleRemoveActions"
|
||||
(action)="
|
||||
annotationActionsService.markAsFalsePositive($event, [annotation], annotationsChanged)
|
||||
"
|
||||
*ngIf="
|
||||
annotationPermissions.canMarkTextOnlyAsFalsePositive &&
|
||||
!annotationPermissions.canPerformMultipleRemoveActions
|
||||
"
|
||||
icon="red:thumb-down"
|
||||
tooltip="annotation-actions.remove-annotation.false-positive"
|
||||
tooltipPosition="before"
|
||||
@ -30,7 +41,9 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.acceptSuggestion($event, [annotation], annotationsChanged)"
|
||||
(action)="
|
||||
annotationActionsService.acceptSuggestion($event, [annotation], annotationsChanged)
|
||||
"
|
||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||
icon="red:check"
|
||||
tooltip="annotation-actions.accept-suggestion.label"
|
||||
@ -40,7 +53,9 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.undoDirectAction($event, [annotation], annotationsChanged)"
|
||||
(action)="
|
||||
annotationActionsService.undoDirectAction($event, [annotation], annotationsChanged)
|
||||
"
|
||||
*ngIf="annotationPermissions.canUndo"
|
||||
icon="red:undo"
|
||||
tooltip="annotation-actions.undo"
|
||||
@ -70,7 +85,9 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="annotationActionsService.rejectSuggestion($event, [annotation], annotationsChanged)"
|
||||
(action)="
|
||||
annotationActionsService.rejectSuggestion($event, [annotation], annotationsChanged)
|
||||
"
|
||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||
icon="red:close"
|
||||
tooltip="annotation-actions.reject-suggestion"
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<redaction-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
*ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere && permissions.canNotPerformMultipleRemoveActions"
|
||||
*ngIf="
|
||||
permissions.canRemoveOrSuggestToRemoveOnlyHere &&
|
||||
permissions.canNotPerformMultipleRemoveActions
|
||||
"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[type]="btnType"
|
||||
icon="red:trash"
|
||||
@ -21,16 +24,36 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<mat-menu #menu="matMenu" (closed)="onMenuClosed()" xPosition="before">
|
||||
<div (click)="suggestRemoveAnnotations($event, true)" *ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary" mat-menu-item>
|
||||
<redaction-annotation-icon [color]="dictionaryColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
||||
<div
|
||||
(click)="suggestRemoveAnnotations($event, true)"
|
||||
*ngIf="permissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||
mat-menu-item
|
||||
>
|
||||
<redaction-annotation-icon
|
||||
[color]="dictionaryColor"
|
||||
[label]="'S'"
|
||||
[type]="'rhombus'"
|
||||
></redaction-annotation-icon>
|
||||
<div [translate]="'annotation-actions.remove-annotation.remove-from-dict'"></div>
|
||||
</div>
|
||||
<div (click)="suggestRemoveAnnotations($event, false)" *ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere" mat-menu-item>
|
||||
<redaction-annotation-icon [color]="suggestionColor" [label]="'S'" [type]="'rhombus'"></redaction-annotation-icon>
|
||||
<div
|
||||
(click)="suggestRemoveAnnotations($event, false)"
|
||||
*ngIf="permissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||
mat-menu-item
|
||||
>
|
||||
<redaction-annotation-icon
|
||||
[color]="suggestionColor"
|
||||
[label]="'S'"
|
||||
[type]="'rhombus'"
|
||||
></redaction-annotation-icon>
|
||||
<div translate="annotation-actions.remove-annotation.only-here"></div>
|
||||
</div>
|
||||
|
||||
<div (click)="markAsFalsePositive($event)" *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>
|
||||
<div translate="annotation-actions.remove-annotation.false-positive"></div>
|
||||
</div>
|
||||
|
||||
@ -65,20 +65,44 @@ export class AnnotationRemoveActionsComponent {
|
||||
|
||||
suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||
$event.stopPropagation();
|
||||
this._annotationActionsService.suggestRemoveAnnotation($event, this.annotations, removeFromDict, this.annotationsChanged);
|
||||
this._annotationActionsService.suggestRemoveAnnotation(
|
||||
$event,
|
||||
this.annotations,
|
||||
removeFromDict,
|
||||
this.annotationsChanged
|
||||
);
|
||||
}
|
||||
|
||||
markAsFalsePositive($event) {
|
||||
this._annotationActionsService.markAsFalsePositive($event, this.annotations, this.annotationsChanged);
|
||||
this._annotationActionsService.markAsFalsePositive(
|
||||
$event,
|
||||
this.annotations,
|
||||
this.annotationsChanged
|
||||
);
|
||||
}
|
||||
|
||||
private _setPermissions() {
|
||||
this.permissions = {
|
||||
canRemoveOrSuggestToRemoveOnlyHere: this._annotationsPermissions(['canRemoveOrSuggestToRemoveOnlyHere'], true),
|
||||
canPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], true),
|
||||
canNotPerformMultipleRemoveActions: this._annotationsPermissions(['canPerformMultipleRemoveActions'], false),
|
||||
canRemoveOrSuggestToRemoveFromDictionary: this._annotationsPermissions(['canRemoveOrSuggestToRemoveFromDictionary'], true),
|
||||
canMarkAsFalsePositive: this._annotationsPermissions(['canMarkAsFalsePositive', 'canMarkTextOnlyAsFalsePositive'], true)
|
||||
canRemoveOrSuggestToRemoveOnlyHere: this._annotationsPermissions(
|
||||
['canRemoveOrSuggestToRemoveOnlyHere'],
|
||||
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,
|
||||
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;
|
||||
}, true);
|
||||
}
|
||||
|
||||
@ -15,7 +15,10 @@
|
||||
type="dark-bg"
|
||||
></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>
|
||||
|
||||
<redaction-circle-button
|
||||
(action)="setToUnderApproval()"
|
||||
@ -26,7 +29,13 @@
|
||||
>
|
||||
</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>
|
||||
|
||||
<!-- Approved-->
|
||||
@ -41,10 +50,22 @@
|
||||
</redaction-circle-button>
|
||||
|
||||
<!-- 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 (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
|
||||
(action)="reanalyse()"
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { ChangeDetectorRef, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.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 { FileStatusWrapper } from '@models/file/file-status.wrapper';
|
||||
import { FileActionService } from '../../services/file-action.service';
|
||||
@ -39,11 +42,19 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
}
|
||||
|
||||
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() {
|
||||
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() {
|
||||
@ -51,19 +62,31 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
}
|
||||
|
||||
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 canAssign() {
|
||||
return this.selectedFiles.reduce((acc, file) => acc && this._permissionsService.canAssignReviewer(file), true);
|
||||
return this.selectedFiles.reduce(
|
||||
(acc, file) => acc && this._permissionsService.canAssignReviewer(file),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
||||
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() {
|
||||
@ -72,35 +95,55 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
|
||||
// Under review
|
||||
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
|
||||
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
|
||||
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() {
|
||||
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
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.loading = true;
|
||||
this._dialogService.openDeleteFilesDialog(null, this._appStateService.activeProject.project.projectId, this.selectedFileIds, () => {
|
||||
this.reload.emit();
|
||||
this.loading = false;
|
||||
this.selectedFileIds.splice(0, this.selectedFileIds.length);
|
||||
});
|
||||
this._dialogService.openDeleteFilesDialog(
|
||||
null,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this.selectedFileIds,
|
||||
() => {
|
||||
this.reload.emit();
|
||||
this.loading = false;
|
||||
this.selectedFileIds.splice(0, this.selectedFileIds.length);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
assign() {
|
||||
@ -112,8 +155,15 @@ export class ProjectOverviewBulkActionsComponent {
|
||||
}
|
||||
|
||||
async reanalyse() {
|
||||
const fileIds = this.selectedFiles.filter((file) => this._permissionsService.fileRequiresReanalysis(file)).map((file) => file.fileId);
|
||||
this._performBulkAction(this._reanalysisControllerService.reanalyzeFilesForProject(fileIds, this._appStateService.activeProject.projectId));
|
||||
const fileIds = this.selectedFiles
|
||||
.filter((file) => this._permissionsService.fileRequiresReanalysis(file))
|
||||
.map((file) => file.fileId);
|
||||
this._performBulkAction(
|
||||
this._reanalysisControllerService.reanalyzeFilesForProject(
|
||||
fileIds,
|
||||
this._appStateService.activeProject.projectId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
ocr() {
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
<div class="wrapper">
|
||||
<ng-container *ngIf="expanded">
|
||||
<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">
|
||||
<mat-icon [svgIcon]="isCommentOwner(comment) ? 'red:comment-fill' : 'red:comment'"></mat-icon>
|
||||
<div
|
||||
[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 (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>
|
||||
</div>
|
||||
|
||||
@ -20,9 +30,14 @@
|
||||
{{
|
||||
expanded
|
||||
? translateService.instant('comments.hide-comments')
|
||||
: translateService.instant(annotation.comments.length === 1 ? 'comments.comment' : 'comments.comments', {
|
||||
count: annotation.comments.length
|
||||
})
|
||||
: translateService.instant(
|
||||
annotation.comments.length === 1
|
||||
? 'comments.comment'
|
||||
: 'comments.comments',
|
||||
{
|
||||
count: annotation.comments.length
|
||||
}
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
@ -32,14 +47,33 @@
|
||||
></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">
|
||||
<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>
|
||||
</form>
|
||||
|
||||
<div *ngIf="addingComment" class="comment-actions-container">
|
||||
<redaction-circle-button (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>
|
||||
<redaction-circle-button
|
||||
(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>
|
||||
|
||||
@ -58,25 +58,29 @@ export class CommentsComponent {
|
||||
addComment(): void {
|
||||
const value = this.commentForm.value.comment;
|
||||
if (value) {
|
||||
this._manualAnnotationService.addComment(value, this.annotation.id).subscribe((commentResponse) => {
|
||||
this.annotation.comments.push({
|
||||
text: value,
|
||||
id: commentResponse.commentId,
|
||||
user: this._userService.userId
|
||||
this._manualAnnotationService
|
||||
.addComment(value, this.annotation.id)
|
||||
.subscribe((commentResponse) => {
|
||||
this.annotation.comments.push({
|
||||
text: value,
|
||||
id: commentResponse.commentId,
|
||||
user: this._userService.userId
|
||||
});
|
||||
});
|
||||
});
|
||||
this.commentForm.reset();
|
||||
this.toggleAddingComment();
|
||||
}
|
||||
}
|
||||
|
||||
deleteComment(comment: Comment): void {
|
||||
this._manualAnnotationService.deleteComment(comment.id, this.annotation.id).subscribe(() => {
|
||||
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
||||
if (!this.annotation.comments.length) {
|
||||
this.expanded = false;
|
||||
}
|
||||
});
|
||||
this._manualAnnotationService
|
||||
.deleteComment(comment.id, this.annotation.id)
|
||||
.subscribe(() => {
|
||||
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
||||
if (!this.annotation.comments.length) {
|
||||
this.expanded = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isCommentOwner(comment: Comment): boolean {
|
||||
|
||||
@ -26,19 +26,31 @@
|
||||
<div class="section small-label stats-subtitle">
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
<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 *ngIf="project.project.dueDate">
|
||||
<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>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
|
||||
@ -14,7 +14,10 @@ export class DocumentInfoComponent {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -31,10 +31,16 @@
|
||||
<!-- assign-->
|
||||
<redaction-circle-button
|
||||
(action)="assignReviewer($event, fileStatus)"
|
||||
*ngIf="permissionsService.canAssignReviewer(fileStatus) && screen === 'project-overview'"
|
||||
*ngIf="
|
||||
permissionsService.canAssignReviewer(fileStatus) && screen === 'project-overview'
|
||||
"
|
||||
[icon]="permissionsService.isOwner() ? 'red:assign' : 'red:assign-me'"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="permissionsService.isApprover() ? 'project-overview.assign' : 'project-overview.assign-me'"
|
||||
[tooltip]="
|
||||
permissionsService.isApprover()
|
||||
? 'project-overview.assign'
|
||||
: 'project-overview.assign-me'
|
||||
"
|
||||
[type]="buttonType"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
@ -86,7 +92,11 @@
|
||||
*ngIf="permissionsService.isReadyForApproval(fileStatus)"
|
||||
[disabled]="!permissionsService.canApprove(fileStatus)"
|
||||
[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"
|
||||
icon="red:approved"
|
||||
>
|
||||
|
||||
@ -38,7 +38,9 @@ export class FileActionsComponent implements OnInit {
|
||||
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';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
@ -60,14 +62,21 @@ export class FileActionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
|
||||
this._dialogService.openDeleteFilesDialog($event, fileStatusWrapper.projectId, [fileStatusWrapper.fileId], () => {
|
||||
this.actionPerformed.emit('delete');
|
||||
});
|
||||
this._dialogService.openDeleteFilesDialog(
|
||||
$event,
|
||||
fileStatusWrapper.projectId,
|
||||
[fileStatusWrapper.fileId],
|
||||
() => {
|
||||
this.actionPerformed.emit('delete');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async assignReviewer($event: MouseEvent, file: FileStatusWrapper) {
|
||||
$event.stopPropagation();
|
||||
await this._fileActionService.assignProjectReviewerFromOverview(file, () => this.actionPerformed.emit('assign-reviewer'));
|
||||
await this._fileActionService.assignProjectReviewerFromOverview(file, () =>
|
||||
this.actionPerformed.emit('assign-reviewer')
|
||||
);
|
||||
}
|
||||
|
||||
reanalyseFile($event: MouseEvent, fileStatusWrapper: FileStatusWrapper, priority = -1) {
|
||||
@ -98,12 +107,20 @@ export class FileActionsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
setFileUnderReview($event: MouseEvent, fileStatus: FileStatusWrapper, ignoreDialogChanges = false) {
|
||||
setFileUnderReview(
|
||||
$event: MouseEvent,
|
||||
fileStatus: FileStatusWrapper,
|
||||
ignoreDialogChanges = false
|
||||
) {
|
||||
$event.stopPropagation();
|
||||
// this._fileActionService.setFileUnderReview(fileStatus).subscribe(() => {
|
||||
// this.reloadProjects('set-review');
|
||||
// });
|
||||
this._fileActionService.assignProjectReviewer(fileStatus, () => this.actionPerformed.emit('assign-reviewer'), ignoreDialogChanges);
|
||||
this._fileActionService.assignProjectReviewer(
|
||||
fileStatus,
|
||||
() => this.actionPerformed.emit('assign-reviewer'),
|
||||
ignoreDialogChanges
|
||||
);
|
||||
}
|
||||
|
||||
reloadProjects(action: string) {
|
||||
@ -116,6 +133,8 @@ export class FileActionsComponent implements OnInit {
|
||||
$event.stopPropagation();
|
||||
await this._fileActionService.toggleAnalysis(this.fileStatus).toPromise();
|
||||
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"
|
||||
></redaction-annotation-remove-actions>
|
||||
</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 [class.multi-select-active]="multiSelectActive" class="annotations-wrapper">
|
||||
<div
|
||||
@ -78,13 +82,24 @@
|
||||
<div style="overflow: hidden; width: 100%">
|
||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
||||
><span translate="page"></span> {{ activeViewerPage }} -
|
||||
{{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span
|
||||
[translate]="
|
||||
displayedAnnotations[activeViewerPage]?.annotations?.length === 1
|
||||
? 'annotation'
|
||||
: 'annotations'
|
||||
"
|
||||
></span
|
||||
></span>
|
||||
|
||||
<div *ngIf="multiSelectActive">
|
||||
<div (click)="selectAllOnActivePage()" class="all-caps-label primary pointer">All</div>
|
||||
<div (click)="deselectAllOnActivePage()" class="all-caps-label primary pointer">None</div>
|
||||
<div (click)="selectAllOnActivePage()" class="all-caps-label primary pointer">
|
||||
All
|
||||
</div>
|
||||
<div (click)="deselectAllOnActivePage()" class="all-caps-label primary pointer">
|
||||
None
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -98,7 +113,12 @@
|
||||
tabindex="1"
|
||||
>
|
||||
<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">
|
||||
<redaction-icon-button
|
||||
(action)="jumpToPreviousWithAnnotations()"
|
||||
@ -109,7 +129,9 @@
|
||||
></redaction-icon-button>
|
||||
<redaction-icon-button
|
||||
(action)="jumpToNextWithAnnotations()"
|
||||
[disabled]="activeViewerPage >= displayedPages[displayedPages.length - 1]"
|
||||
[disabled]="
|
||||
activeViewerPage >= displayedPages[displayedPages.length - 1]
|
||||
"
|
||||
class="mt-8"
|
||||
icon="red:nav-next"
|
||||
text="file-preview.tabs.annotations.jump-to-next"
|
||||
@ -129,21 +151,32 @@
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
<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">
|
||||
<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>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
||||
<div
|
||||
*ngIf="
|
||||
annotation.dictionary &&
|
||||
annotation.dictionary !== 'manual'
|
||||
"
|
||||
>
|
||||
<strong
|
||||
><span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.dictionary | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.content && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
||||
<strong><span translate="content"></span>: </strong
|
||||
>{{ annotation.content }}
|
||||
</div>
|
||||
{{ annotation.id }}
|
||||
</div>
|
||||
@ -155,7 +188,9 @@
|
||||
</ng-container>
|
||||
<div class="active-icon-marker-container">
|
||||
<redaction-round-checkbox
|
||||
*ngIf="multiSelectActive && annotationIsSelected(annotation)"
|
||||
*ngIf="
|
||||
multiSelectActive && annotationIsSelected(annotation)
|
||||
"
|
||||
[active]="true"
|
||||
></redaction-round-checkbox>
|
||||
</div>
|
||||
@ -170,16 +205,25 @@
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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 }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
||||
<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>
|
||||
</ng-container>
|
||||
</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 { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
@ -27,7 +37,9 @@ export class FileWorkloadComponent {
|
||||
@Input() hideSkipped: boolean;
|
||||
@Input() annotationActionsTemplate: TemplateRef<any>;
|
||||
@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() selectPage = new EventEmitter<number>();
|
||||
@Output() toggleSkipped = new EventEmitter<any>();
|
||||
@ -39,7 +51,10 @@ export class FileWorkloadComponent {
|
||||
@ViewChild('annotationsElement') private _annotationsElement: 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[];
|
||||
|
||||
@ -68,7 +83,10 @@ export class FileWorkloadComponent {
|
||||
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) {
|
||||
scrollIntoView(elements[0], {
|
||||
behavior: 'smooth',
|
||||
@ -88,7 +106,9 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
pageHasSelection(page: number) {
|
||||
return this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page);
|
||||
return (
|
||||
this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page)
|
||||
);
|
||||
}
|
||||
|
||||
selectAllOnActivePage() {
|
||||
@ -103,7 +123,11 @@ export class FileWorkloadComponent {
|
||||
|
||||
@debounce(0)
|
||||
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.computeQuickNavButtonsState();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
@ -111,7 +135,8 @@ export class FileWorkloadComponent {
|
||||
|
||||
computeQuickNavButtonsState() {
|
||||
setTimeout(() => {
|
||||
const element: HTMLElement = this._quickNavigationElement.nativeElement.querySelector(`#pages`);
|
||||
const element: HTMLElement =
|
||||
this._quickNavigationElement.nativeElement.querySelector(`#pages`);
|
||||
const { scrollTop, scrollHeight, clientHeight } = element;
|
||||
this.quickScrollFirstEnabled = scrollTop !== 0;
|
||||
this.quickScrollLastEnabled = scrollHeight !== scrollTop + clientHeight;
|
||||
@ -126,13 +151,20 @@ export class FileWorkloadComponent {
|
||||
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
}
|
||||
this.selectAnnotations.emit({ annotations: [annotation], multiSelect: this.multiSelectActive });
|
||||
this.selectAnnotations.emit({
|
||||
annotations: [annotation],
|
||||
multiSelect: this.multiSelectActive
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
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;
|
||||
}
|
||||
|
||||
@ -143,7 +175,8 @@ export class FileWorkloadComponent {
|
||||
|
||||
if ($event.key === 'ArrowRight') {
|
||||
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
|
||||
if (!this.pagesPanelActive && !this.multiSelectActive) {
|
||||
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
||||
@ -152,7 +185,8 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
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) {
|
||||
this._navigateAnnotations($event);
|
||||
}
|
||||
@ -171,7 +205,9 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -180,13 +216,18 @@ export class FileWorkloadComponent {
|
||||
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
|
||||
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);
|
||||
}
|
||||
|
||||
scrollQuickNavigation() {
|
||||
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);
|
||||
}
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
|
||||
@ -210,7 +251,10 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -229,39 +273,53 @@ export class FileWorkloadComponent {
|
||||
|
||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||
if (
|
||||
(!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
|
||||
(!this._firstSelectedAnnotation ||
|
||||
this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
|
||||
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) {
|
||||
if (!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) {
|
||||
if (
|
||||
!this._firstSelectedAnnotation ||
|
||||
this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber
|
||||
) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
if (pageIdx !== -1) {
|
||||
// Displayed page has annotations
|
||||
this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
|
||||
this.selectAnnotations.emit([
|
||||
this.displayedAnnotations[this.activeViewerPage].annotations[0]
|
||||
]);
|
||||
} else {
|
||||
// Displayed page doesn't have annotations
|
||||
if ($event.key === 'ArrowDown') {
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([this.displayedAnnotations[nextPage].annotations[0]]);
|
||||
this.selectAnnotations.emit([
|
||||
this.displayedAnnotations[nextPage].annotations[0]
|
||||
]);
|
||||
} else {
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
||||
this.selectAnnotations.emit([
|
||||
prevPageAnnotations[prevPageAnnotations.length - 1]
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const page = this._firstSelectedAnnotation.pageNumber;
|
||||
const pageIdx = this.displayedPages.indexOf(page);
|
||||
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 (idx + 1 !== annotationsOnPage.length) {
|
||||
@ -269,7 +327,8 @@ export class FileWorkloadComponent {
|
||||
this.selectAnnotations.emit([annotationsOnPage[idx + 1]]);
|
||||
} else if (pageIdx + 1 < this.displayedPages.length) {
|
||||
// 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.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([nextPageAnnotations[0]]);
|
||||
@ -280,10 +339,13 @@ export class FileWorkloadComponent {
|
||||
this.selectAnnotations.emit([annotationsOnPage[idx - 1]]);
|
||||
} else if (pageIdx) {
|
||||
// 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.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
|
||||
this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
|
||||
this.selectAnnotations.emit([
|
||||
prevPageAnnotations[prevPageAnnotations.length - 1]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -345,7 +407,9 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,39 @@
|
||||
<div class="needs-work">
|
||||
<redaction-annotation-icon *ngIf="reanalysisRequired()" [color]="analysisColor" label="A" type="square"></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>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="reanalysisRequired()"
|
||||
[color]="analysisColor"
|
||||
label="A"
|
||||
type="square"
|
||||
></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>
|
||||
</div>
|
||||
|
||||
@ -12,7 +12,10 @@ import { ProjectWrapper } from '@state/model/project.wrapper';
|
||||
export class NeedsWorkBadgeComponent {
|
||||
@Input() needsWorkInput: FileStatusWrapper | ProjectWrapper;
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _permissionsService: PermissionsService) {}
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _permissionsService: PermissionsService
|
||||
) {}
|
||||
|
||||
get suggestionColor() {
|
||||
return this._getDictionaryColor('suggestion');
|
||||
@ -47,7 +50,10 @@ export class NeedsWorkBadgeComponent {
|
||||
}
|
||||
|
||||
get hasAnnotationComments(): boolean {
|
||||
return this.needsWorkInput instanceof FileStatusWrapper && (<any>this.needsWorkInput).hasAnnotationComments;
|
||||
return (
|
||||
this.needsWorkInput instanceof FileStatusWrapper &&
|
||||
(<any>this.needsWorkInput).hasAnnotationComments
|
||||
);
|
||||
}
|
||||
|
||||
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 { AppStateService } from '@state/app-state.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -95,15 +104,25 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
|
||||
|
||||
private _markPageRead() {
|
||||
this._viewedPagesControllerService
|
||||
.addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId)
|
||||
.addPage(
|
||||
{ page: this.number },
|
||||
this._appStateService.activeProjectId,
|
||||
this._appStateService.activeFileId
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.viewedPages?.pages?.push(this.number);
|
||||
});
|
||||
}
|
||||
|
||||
private _markPageUnread() {
|
||||
this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => {
|
||||
this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
|
||||
});
|
||||
this._viewedPagesControllerService
|
||||
.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 WebViewer, { Annotations, Tools, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -72,7 +85,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
this.instance.annotManager.deselectAllAnnotations();
|
||||
}
|
||||
|
||||
selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
|
||||
selectAnnotations(
|
||||
$event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
|
||||
) {
|
||||
let annotations: AnnotationWrapper[];
|
||||
let multiSelect: boolean;
|
||||
if ($event instanceof Array) {
|
||||
@ -87,14 +102,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
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.navigateToPage(annotations[0].pageNumber);
|
||||
this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
|
||||
}
|
||||
|
||||
deselectAnnotations(annotations: AnnotationWrapper[]) {
|
||||
this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)));
|
||||
this.instance.annotManager.deselectAnnotations(
|
||||
annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id))
|
||||
);
|
||||
}
|
||||
|
||||
navigateToPage(pageNumber: number) {
|
||||
@ -107,7 +126,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
setInitialViewerState() {
|
||||
// viewer init
|
||||
this.instance.setFitMode('FitPage');
|
||||
const instanceDisplayMode = this.instance.docViewer.getDisplayModeManager().getDisplayMode();
|
||||
const instanceDisplayMode = this.instance.docViewer
|
||||
.getDisplayModeManager()
|
||||
.getDisplayMode();
|
||||
instanceDisplayMode.mode = 'Single';
|
||||
this.instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
|
||||
}
|
||||
@ -127,20 +148,28 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
this._configureTextPopup();
|
||||
|
||||
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') {
|
||||
this._toggleRectangleAnnotationAction(true);
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
|
||||
if (
|
||||
annotations.length === 1 &&
|
||||
annotations[0].ToolName === 'AnnotationCreateRectangle'
|
||||
) {
|
||||
instance.annotManager.selectAnnotations(annotations);
|
||||
}
|
||||
});
|
||||
@ -183,7 +212,9 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
instance.iframeWindow.addEventListener('visibilityChanged', (event: any) => {
|
||||
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(() => {
|
||||
inputElement.value = '';
|
||||
}, 0);
|
||||
@ -253,43 +284,58 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
private _configureAnnotationSpecificActions(viewerAnnotations: Annotations.Annotation[]) {
|
||||
console.log('configure actions', viewerAnnotations);
|
||||
if (this.canPerformActions) {
|
||||
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));
|
||||
if (!this.canPerformActions) {
|
||||
return;
|
||||
}
|
||||
|
||||
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() {
|
||||
@ -297,12 +343,17 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
type: 'actionButton',
|
||||
dataElement: 'add-rectangle',
|
||||
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: () => {
|
||||
const selectedAnnotations = this.instance.annotManager.getSelectedAnnotations();
|
||||
const activeAnnotation = selectedAnnotations[0];
|
||||
const activePage = selectedAnnotations[0].getPageNumber();
|
||||
const quad = this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance);
|
||||
const quad = this._annotationDrawService.annotationToQuads(
|
||||
activeAnnotation,
|
||||
this.instance
|
||||
);
|
||||
const quadsObject = {};
|
||||
quadsObject[activePage] = [quad];
|
||||
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
|
||||
@ -311,7 +362,15 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
this.instance.disableElements(['shapeToolGroupButton']);
|
||||
this.instance.enableElements(['shapeToolGroupButton']);
|
||||
// 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',
|
||||
dataElement: 'add-false-positive',
|
||||
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: () => {
|
||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||
const text = this.instance.docViewer.getSelectedText();
|
||||
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',
|
||||
dataElement: 'add-dictionary',
|
||||
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: () => {
|
||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||
const text = this.instance.docViewer.getSelectedText();
|
||||
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',
|
||||
dataElement: 'add-redaction',
|
||||
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: () => {
|
||||
const selectedQuads = this.instance.docViewer.getSelectedTextQuads();
|
||||
const text = this.instance.docViewer.getSelectedText();
|
||||
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();
|
||||
@ -386,7 +469,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
this.instance.setToolMode('AnnotationEdit');
|
||||
if (this.canPerformActions) {
|
||||
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) {
|
||||
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, '');
|
||||
const entry: ManualRedactionEntry = { positions: [] };
|
||||
for (const key of Object.keys(quads)) {
|
||||
for (const quad of quads[key]) {
|
||||
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;
|
||||
|
||||
@ -53,7 +53,11 @@
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -61,37 +65,63 @@
|
||||
<div [class.mt-24]="!hasFiles" class="pb-32 small-label stats-subtitle">
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
<mat-icon svgIcon="red:pages"></mat-icon>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
<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>
|
||||
</div>
|
||||
<div *ngIf="appStateService.activeProject.project.dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
<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>
|
||||
</div>
|
||||
<div>
|
||||
<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 *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>
|
||||
<span>{{ 'project-overview.project-details.dictionary' | translate }} </span>
|
||||
</div>
|
||||
|
||||
@ -51,10 +51,17 @@ export class ProjectDetailsComponent implements OnInit {
|
||||
const groups = groupBy(this.appStateService.activeProject?.files, 'status');
|
||||
this.documentsChartData = [];
|
||||
for (const key of Object.keys(groups)) {
|
||||
this.documentsChartData.push({ value: groups[key].length, color: key, label: key, key: key });
|
||||
this.documentsChartData.push({
|
||||
value: groups[key].length,
|
||||
color: key,
|
||||
label: key,
|
||||
key: key
|
||||
});
|
||||
}
|
||||
this.documentsChartData.sort((a, b) => StatusSorter[a.key] - StatusSorter[b.key]);
|
||||
this.documentsChartData = this.translateChartService.translateStatus(this.documentsChartData);
|
||||
this.documentsChartData = this.translateChartService.translateStatus(
|
||||
this.documentsChartData
|
||||
);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,5 +36,9 @@
|
||||
>
|
||||
</redaction-circle-button>
|
||||
|
||||
<redaction-file-download-btn [file]="project.files" [project]="project" type="dark-bg"></redaction-file-download-btn>
|
||||
<redaction-file-download-btn
|
||||
[file]="project.files"
|
||||
[project]="project"
|
||||
type="dark-bg"
|
||||
></redaction-file-download-btn>
|
||||
</div>
|
||||
|
||||
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