auto-formatted entire codebase with prettier
This commit is contained in:
parent
b0a1222409
commit
69d7f48108
14
.prettierrc
14
.prettierrc
@ -1,3 +1,15 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
"useTabs": false,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"overrides": [
|
||||
{
|
||||
"files": "{apps}/**/*.html",
|
||||
"options": {
|
||||
"parser": "angular"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
29
README.md
29
README.md
@ -1,7 +1,9 @@
|
||||
# Redaction
|
||||
|
||||
## Swagger Generated Code
|
||||
|
||||
To regnerate http rune swaagger
|
||||
|
||||
```
|
||||
BASE=http://ingress.redaction-timo-dev-401.178.63.47.73.xip.io/
|
||||
URL="$BASE"v2/api-docs?group=redaction-gateway-v1
|
||||
@ -12,19 +14,19 @@ swagger-codegen generate -i "$URL" -l typescript-angular -o /tmp/swagger
|
||||
## To Create a new Stack in rancher
|
||||
|
||||
Goto rancher.iqser.com: Select Cluster `Development`,
|
||||
go to apps, click launch and select `Redaction` from the `dev` section.
|
||||
go to apps, click launch and select `Redaction` from the `dev` section.
|
||||
Add a new name and a new namespace.
|
||||
Select `answers-development.yaml` and add it to answers `Edit as yaml`.
|
||||
|
||||
For HTTPS / Cloudflare domain go to `workloads` -> `Loadbalancing` -> `select your stack`
|
||||
For HTTPS / Cloudflare domain go to `workloads` -> `Loadbalancing` -> `select your stack`
|
||||
Add cloudflare certificate and specify a hostname to use `timo-redaction-dev.iqser.cloud`
|
||||
|
||||
## Keycloak Staging Config
|
||||
|
||||
keycloak:
|
||||
authServerUrl: 'https://redkc-staging.iqser.cloud/auth'
|
||||
client:
|
||||
secret: 'a4e8aa56-03b0-4e6b-b822-8ac1f41280c4'
|
||||
keycloak:
|
||||
authServerUrl: 'https://redkc-staging.iqser.cloud/auth'
|
||||
client:
|
||||
secret: 'a4e8aa56-03b0-4e6b-b822-8ac1f41280c4'
|
||||
|
||||
## Default Testing URL
|
||||
|
||||
@ -35,13 +37,12 @@ timo-redaction-dev.iqser.cloud
|
||||
|
||||
## Test Users
|
||||
|
||||
| username | role | comment |
|
||||
| --- | --- | --- |
|
||||
| guest | | cannot use the application |
|
||||
| user | RED_USER | |
|
||||
| red_manager | RED_MANAGER | |
|
||||
| useradmin | RED_ADMIN, RED_USER | has super power ! |
|
||||
| manageradmin | RED_ADMIN RED_MANAGER RED_USER | has super super power ! |
|
||||
| username | role | comment |
|
||||
| ------------ | ------------------------------ | -------------------------- |
|
||||
| guest | | cannot use the application |
|
||||
| user | RED_USER | |
|
||||
| red_manager | RED_MANAGER | |
|
||||
| useradmin | RED_ADMIN, RED_USER | has super power ! |
|
||||
| manageradmin | RED_ADMIN RED_MANAGER RED_USER | has super super power ! |
|
||||
|
||||
Password for all users is `OsloImWinter`
|
||||
|
||||
|
||||
306
angular.json
306
angular.json
@ -1,171 +1,157 @@
|
||||
{
|
||||
"version": 1,
|
||||
"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/"
|
||||
},
|
||||
"apps/red-ui/src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"apps/red-ui/src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/@pdftron/webviewer/webviewer.min.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/red-ui/src/environments/environment.ts",
|
||||
"with": "apps/red-ui/src/environments/environment.prod.ts"
|
||||
"version": 1,
|
||||
"projects": {
|
||||
"red-ui": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
"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/"
|
||||
},
|
||||
"apps/red-ui/src/manifest.webmanifest"
|
||||
],
|
||||
"styles": ["apps/red-ui/src/styles.scss"],
|
||||
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.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,
|
||||
"extractCss": true,
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "red-ui:build",
|
||||
"proxyConfig": "apps/red-ui/proxy.conf.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "red-ui:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "red-ui:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": ["apps/red-ui/tsconfig.app.json"],
|
||||
"exclude": ["**/node_modules/**", "!apps/red-ui/**/*"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "apps/red-ui/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "apps/red-ui/ngsw-config.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "red-ui:build",
|
||||
"proxyConfig": "apps/red-ui/proxy.conf.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "red-ui:build:production"
|
||||
"red-ui-http": {
|
||||
"projectType": "library",
|
||||
"root": "libs/red-ui-http",
|
||||
"sourceRoot": "libs/red-ui-http/src",
|
||||
"prefix": "redaction",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "libs/red-ui-http/tsconfig.lib.json",
|
||||
"project": "libs/red-ui-http/ng-package.json"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": ["libs/red-ui-http/tsconfig.lib.json"],
|
||||
"exclude": ["**/node_modules/**", "!libs/red-ui-http/**/*"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "libs/red-ui-http/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "red-ui:build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"apps/red-ui/tsconfig.app.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
"!apps/red-ui/**/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "apps/red-ui/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"red-ui-http": {
|
||||
"projectType": "library",
|
||||
"root": "libs/red-ui-http",
|
||||
"sourceRoot": "libs/red-ui-http/src",
|
||||
"prefix": "redaction",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||
"options": {
|
||||
"tsConfig": "libs/red-ui-http/tsconfig.lib.json",
|
||||
"project": "libs/red-ui-http/ng-package.json"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"libs/red-ui-http/tsconfig.lib.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
"!libs/red-ui-http/**/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@nrwl/jest:jest",
|
||||
"options": {
|
||||
"jestConfig": "libs/red-ui-http/jest.config.js",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false
|
||||
},
|
||||
"schematics": {
|
||||
"@nrwl/angular:application": {
|
||||
"unitTestRunner": "jest",
|
||||
"e2eTestRunner": "cypress"
|
||||
"cli": {
|
||||
"defaultCollection": "@nrwl/angular",
|
||||
"analytics": false
|
||||
},
|
||||
"@nrwl/angular:library": {
|
||||
"unitTestRunner": "jest"
|
||||
}
|
||||
},
|
||||
"defaultProject": "red-ui"
|
||||
"schematics": {
|
||||
"@nrwl/angular:application": {
|
||||
"unitTestRunner": "jest",
|
||||
"e2eTestRunner": "cypress"
|
||||
},
|
||||
"@nrwl/angular:library": {
|
||||
"unitTestRunner": "jest"
|
||||
}
|
||||
},
|
||||
"defaultProject": "red-ui"
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
module.exports = {
|
||||
name: 'red-ui',
|
||||
preset: '../../jest.config.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
astTransformers: [
|
||||
'jest-preset-angular/build/InlineFilesTransformer',
|
||||
'jest-preset-angular/build/StripStylesTransformer',
|
||||
],
|
||||
name: 'red-ui',
|
||||
preset: '../../jest.config.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
astTransformers: [
|
||||
'jest-preset-angular/build/InlineFilesTransformer',
|
||||
'jest-preset-angular/build/StripStylesTransformer'
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/red-ui',
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/AngularSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/HTMLCommentSerializer.js',
|
||||
],
|
||||
coverageDirectory: '../../coverage/apps/red-ui',
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/AngularSnapshotSerializer.js',
|
||||
'jest-preset-angular/build/HTMLCommentSerializer.js'
|
||||
]
|
||||
};
|
||||
|
||||
@ -1,30 +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)"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,56 +1,56 @@
|
||||
{
|
||||
"/project": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/reanalyze": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/upload": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/user": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/download": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/delete": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/status": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/dictionary": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/manualRedaction": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
}
|
||||
"/project": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/reanalyze": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/upload": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/user": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/download": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/delete": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/status": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/dictionary": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
},
|
||||
"/manualRedaction": {
|
||||
"target": "https://timo-redaction-dev.iqser.cloud/",
|
||||
"secure": false,
|
||||
"changeOrigin": true,
|
||||
"logLevel": "debug"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,11 @@
|
||||
import {NgModule} from "@angular/core";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
import {AppConfigService} from "./app-config.service";
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { AppConfigService } from './app-config.service';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
],
|
||||
providers: [
|
||||
AppConfigService,
|
||||
],
|
||||
declarations: [],
|
||||
imports: [CommonModule, HttpClientModule],
|
||||
providers: [AppConfigService]
|
||||
})
|
||||
export class AppConfigModule {
|
||||
|
||||
}
|
||||
export class AppConfigModule {}
|
||||
|
||||
@ -1,36 +1,34 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {tap} from "rxjs/operators";
|
||||
import {Observable} from "rxjs";
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
export enum AppConfigKey {
|
||||
OAUTH_URL = "OAUTH_URL",
|
||||
OAUTH_CLIENT_ID = "OAUTH_CLIENT_ID",
|
||||
API_URL = "API_URL",
|
||||
PDFTRON_LICENSE ="PDFTRON_LICENSE",
|
||||
ADMIN_CONTACT_NAME="ADMIN_CONTACT_NAME",
|
||||
ADMIN_CONTACT_URL="ADMIN_CONTACT_URL",
|
||||
OAUTH_URL = 'OAUTH_URL',
|
||||
OAUTH_CLIENT_ID = 'OAUTH_CLIENT_ID',
|
||||
API_URL = 'API_URL',
|
||||
PDFTRON_LICENSE = 'PDFTRON_LICENSE',
|
||||
ADMIN_CONTACT_NAME = 'ADMIN_CONTACT_NAME',
|
||||
ADMIN_CONTACT_URL = 'ADMIN_CONTACT_URL'
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AppConfigService {
|
||||
private _config: { [key in AppConfigKey]?: any } = {};
|
||||
|
||||
private _config: { [key in AppConfigKey]?: any } = {};
|
||||
constructor(private readonly _httpClient: HttpClient) {}
|
||||
|
||||
constructor(private readonly _httpClient: HttpClient) {
|
||||
}
|
||||
|
||||
loadAppConfig(): Observable<any> {
|
||||
return this._httpClient.get<any>("/assets/config/config.json").pipe(tap(config => {
|
||||
this._config = config;
|
||||
}));
|
||||
}
|
||||
|
||||
getConfig(key: AppConfigKey, defaultValue?: any) {
|
||||
return this._config[key] ? this._config[key] : defaultValue;
|
||||
}
|
||||
loadAppConfig(): Observable<any> {
|
||||
return this._httpClient.get<any>('/assets/config/config.json').pipe(
|
||||
tap((config) => {
|
||||
this._config = config;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getConfig(key: AppConfigKey, defaultValue?: any) {
|
||||
return this._config[key] ? this._config[key] : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {AppLoadStateService} from "./utils/app-load-state.service";
|
||||
import { Component } from '@angular/core';
|
||||
import { AppLoadStateService } from './utils/app-load-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
selector: 'redaction-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
|
||||
constructor(public appLoadStateService: AppLoadStateService){
|
||||
|
||||
}
|
||||
|
||||
constructor(public appLoadStateService: AppLoadStateService) {}
|
||||
}
|
||||
|
||||
@ -57,146 +57,150 @@ import { AuthErrorComponent } from './screens/auth-error/auth-error.component';
|
||||
import { RedRoleGuard } from './auth/red-role.guard';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { AssignOwnerDialogComponent } from './dialogs/assign-owner-dialog/assign-owner-dialog.component';
|
||||
import {MatDatepickerModule} from "@angular/material/datepicker";
|
||||
import {MatNativeDateModule} from "@angular/material/core";
|
||||
import {MatInputModule} from "@angular/material/input";
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatNativeDateModule } from '@angular/material/core';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
BaseScreenComponent,
|
||||
ProjectListingScreenComponent,
|
||||
ProjectOverviewScreenComponent,
|
||||
AddEditProjectDialogComponent,
|
||||
ConfirmationDialogComponent,
|
||||
FilePreviewScreenComponent,
|
||||
PdfViewerComponent,
|
||||
FileDetailsDialogComponent,
|
||||
ProjectDetailsDialogComponent,
|
||||
AssignOwnerDialogComponent,
|
||||
FullPageLoadingIndicatorComponent,
|
||||
InitialsAvatarComponent,
|
||||
StatusBarComponent,
|
||||
LogoComponent,
|
||||
SimpleDoughnutChartComponent,
|
||||
ManualRedactionDialogComponent,
|
||||
AnnotationIconComponent,
|
||||
AuthErrorComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
HttpClientModule,
|
||||
AuthModule,
|
||||
IconsModule,
|
||||
ApiModule,
|
||||
MatDialogModule,
|
||||
MatNativeDateModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
}),
|
||||
RouterModule.forRoot([
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'ui/projects',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'auth-error',
|
||||
component: AuthErrorComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'ui',
|
||||
component: BaseScreenComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'projects',
|
||||
component: ProjectListingScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
declarations: [
|
||||
AppComponent,
|
||||
BaseScreenComponent,
|
||||
ProjectListingScreenComponent,
|
||||
ProjectOverviewScreenComponent,
|
||||
AddEditProjectDialogComponent,
|
||||
ConfirmationDialogComponent,
|
||||
FilePreviewScreenComponent,
|
||||
PdfViewerComponent,
|
||||
FileDetailsDialogComponent,
|
||||
ProjectDetailsDialogComponent,
|
||||
AssignOwnerDialogComponent,
|
||||
FullPageLoadingIndicatorComponent,
|
||||
InitialsAvatarComponent,
|
||||
StatusBarComponent,
|
||||
LogoComponent,
|
||||
SimpleDoughnutChartComponent,
|
||||
ManualRedactionDialogComponent,
|
||||
AnnotationIconComponent,
|
||||
AuthErrorComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
HttpClientModule,
|
||||
AuthModule,
|
||||
IconsModule,
|
||||
ApiModule,
|
||||
MatDialogModule,
|
||||
MatNativeDateModule,
|
||||
TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useFactory: HttpLoaderFactory,
|
||||
deps: [HttpClient]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId',
|
||||
component: ProjectOverviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}),
|
||||
RouterModule.forRoot([
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'ui/projects',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'auth-error',
|
||||
component: AuthErrorComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'ui',
|
||||
component: BaseScreenComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'projects',
|
||||
component: ProjectListingScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId',
|
||||
component: ProjectOverviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId/file/:fileId',
|
||||
component: FilePreviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'projects/:projectId/file/:fileId',
|
||||
component: FilePreviewScreenComponent,
|
||||
canActivate: [CompositeRouteGuard],
|
||||
data: {
|
||||
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
]),
|
||||
NgpSortModule,
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
MatSnackBarModule,
|
||||
MatTabsModule,
|
||||
MatButtonToggleModule,
|
||||
MatFormFieldModule,
|
||||
ToastrModule.forRoot(),
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
FileUploadModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', {enabled: environment.production}),
|
||||
MatProgressSpinnerModule,
|
||||
MatCheckboxModule,
|
||||
MatListModule,
|
||||
MatDatepickerModule,
|
||||
MatInputModule
|
||||
],
|
||||
providers: [{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
multi: true,
|
||||
useClass: ApiPathInterceptorService
|
||||
}, {
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useFactory: languageInitializer,
|
||||
deps: [LanguageService]
|
||||
}],
|
||||
bootstrap: [AppComponent]
|
||||
]),
|
||||
NgpSortModule,
|
||||
MatToolbarModule,
|
||||
MatButtonModule,
|
||||
MatSlideToggleModule,
|
||||
MatMenuModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
MatSnackBarModule,
|
||||
MatTabsModule,
|
||||
MatButtonToggleModule,
|
||||
MatFormFieldModule,
|
||||
ToastrModule.forRoot(),
|
||||
MatSelectModule,
|
||||
MatSidenavModule,
|
||||
FileUploadModule,
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||
MatProgressSpinnerModule,
|
||||
MatCheckboxModule,
|
||||
MatListModule,
|
||||
MatDatepickerModule,
|
||||
MatInputModule
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
multi: true,
|
||||
useClass: ApiPathInterceptorService
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useFactory: languageInitializer,
|
||||
deps: [LanguageService]
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {
|
||||
|
||||
constructor(private router: Router, private route: ActivatedRoute) {
|
||||
route.queryParamMap.subscribe(queryParams => {
|
||||
if (queryParams.has('code') || queryParams.has('state') || queryParams.has('session_state')) {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
'state': null,
|
||||
'session_state': null,
|
||||
'code': null
|
||||
},
|
||||
queryParamsHandling: 'merge'
|
||||
constructor(private router: Router, private route: ActivatedRoute) {
|
||||
route.queryParamMap.subscribe((queryParams) => {
|
||||
if (
|
||||
queryParams.has('code') ||
|
||||
queryParams.has('state') ||
|
||||
queryParams.has('session_state')
|
||||
) {
|
||||
this.router.navigate([], {
|
||||
queryParams: {
|
||||
state: null,
|
||||
session_state: null,
|
||||
code: null
|
||||
},
|
||||
queryParamsHandling: 'merge'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,30 +1,30 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {ActivatedRouteSnapshot, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {KeycloakAuthGuard, KeycloakService} from "keycloak-angular";
|
||||
import {UserService} from "../user/user.service";
|
||||
import {AppLoadStateService} from "../utils/app-load-state.service";
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
|
||||
import { UserService } from '../user/user.service';
|
||||
import { AppLoadStateService } from '../utils/app-load-state.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthGuard extends KeycloakAuthGuard {
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {
|
||||
super(_router, _keycloak);
|
||||
}
|
||||
|
||||
public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (!this.authenticated) {
|
||||
await this._keycloak.login({
|
||||
redirectUri: window.location.origin + state.url,
|
||||
});
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {
|
||||
super(_router, _keycloak);
|
||||
}
|
||||
|
||||
await this._userService.loadCurrentUser();
|
||||
return true;
|
||||
}
|
||||
public async isAccessAllowed(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (!this.authenticated) {
|
||||
await this._keycloak.login({
|
||||
redirectUri: window.location.origin + state.url
|
||||
});
|
||||
}
|
||||
|
||||
await this._userService.loadCurrentUser();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,59 +1,50 @@
|
||||
import {APP_INITIALIZER, NgModule} from "@angular/core";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {HttpClientModule} from "@angular/common/http";
|
||||
import {AppConfigModule} from "../app-config/app-config.module";
|
||||
|
||||
|
||||
import {KeycloakAngularModule, KeycloakService} from "keycloak-angular";
|
||||
import {AppConfigKey, AppConfigService} from "../app-config/app-config.service";
|
||||
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { AppConfigModule } from '../app-config/app-config.module';
|
||||
|
||||
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
|
||||
import { AppConfigKey, AppConfigService } from '../app-config/app-config.service';
|
||||
|
||||
export function keycloakInitializer(keycloak: KeycloakService, appConfigService: AppConfigService) {
|
||||
return () => {
|
||||
return appConfigService.loadAppConfig().toPromise().then(() => {
|
||||
|
||||
let url = appConfigService.getConfig(AppConfigKey.OAUTH_URL);
|
||||
url = url.replace(/\/$/, ""); // remove trailing slash
|
||||
const realm = url.substring(url.lastIndexOf("/") + 1, url.length);
|
||||
url = url.substr(0, url.lastIndexOf("/realms"));
|
||||
return keycloak.init({
|
||||
config: {
|
||||
url: url,
|
||||
realm: realm,
|
||||
clientId: appConfigService.getConfig(AppConfigKey.OAUTH_CLIENT_ID)
|
||||
},
|
||||
initOptions: {
|
||||
checkLoginIframe: false,
|
||||
onLoad: 'check-sso',
|
||||
silentCheckSsoRedirectUri:
|
||||
window.location.origin + '/assets/oauth/silent-refresh.html',
|
||||
flow: 'standard'
|
||||
},
|
||||
enableBearerInterceptor: true,
|
||||
})
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
return appConfigService
|
||||
.loadAppConfig()
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
let url = appConfigService.getConfig(AppConfigKey.OAUTH_URL);
|
||||
url = url.replace(/\/$/, ''); // remove trailing slash
|
||||
const realm = url.substring(url.lastIndexOf('/') + 1, url.length);
|
||||
url = url.substr(0, url.lastIndexOf('/realms'));
|
||||
return keycloak.init({
|
||||
config: {
|
||||
url: url,
|
||||
realm: realm,
|
||||
clientId: appConfigService.getConfig(AppConfigKey.OAUTH_CLIENT_ID)
|
||||
},
|
||||
initOptions: {
|
||||
checkLoginIframe: false,
|
||||
onLoad: 'check-sso',
|
||||
silentCheckSsoRedirectUri:
|
||||
window.location.origin + '/assets/oauth/silent-refresh.html',
|
||||
flow: 'standard'
|
||||
},
|
||||
enableBearerInterceptor: true
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpClientModule,
|
||||
KeycloakAngularModule,
|
||||
AppConfigModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: keycloakInitializer,
|
||||
multi: true,
|
||||
deps: [KeycloakService, AppConfigService],
|
||||
},
|
||||
],
|
||||
declarations: [],
|
||||
imports: [CommonModule, HttpClientModule, KeycloakAngularModule, AppConfigModule],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: keycloakInitializer,
|
||||
multi: true,
|
||||
deps: [KeycloakService, AppConfigService]
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AuthModule {
|
||||
|
||||
}
|
||||
|
||||
export class AuthModule {}
|
||||
|
||||
@ -1,37 +1,34 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
||||
import {KeycloakService} from "keycloak-angular";
|
||||
import {UserService} from "../user/user.service";
|
||||
import {AppLoadStateService} from "../utils/app-load-state.service";
|
||||
import {Observable} from "rxjs";
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { UserService } from '../user/user.service';
|
||||
import { AppLoadStateService } from '../utils/app-load-state.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
class UrlTree {
|
||||
}
|
||||
class UrlTree {}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class RedRoleGuard implements CanActivate {
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {
|
||||
}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
return new Observable(obs => {
|
||||
if (!this._userService.user.hasAnyREDRoles) {
|
||||
this._router.navigate(['/auth-error']);
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
obs.next(false);
|
||||
obs.complete();
|
||||
} else {
|
||||
obs.next(true);
|
||||
obs.complete();
|
||||
}
|
||||
})
|
||||
}
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _keycloak: KeycloakService,
|
||||
private readonly _appLoadStateService: AppLoadStateService,
|
||||
private readonly _userService: UserService
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
return new Observable((obs) => {
|
||||
if (!this._userService.user.hasAnyREDRoles) {
|
||||
this._router.navigate(['/auth-error']);
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
obs.next(false);
|
||||
obs.complete();
|
||||
} else {
|
||||
obs.next(true);
|
||||
obs.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,20 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="confirmationDialogInput.title"
|
||||
class="dialog-header heading-l">
|
||||
</div>
|
||||
<div [translate]="confirmationDialogInput.title" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<p [translate]="confirmationDialogInput.question"></p>
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<p [translate]="confirmationDialogInput.question"></p>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button (click)="confirm()" color="primary"
|
||||
mat-flat-button>{{confirmationDialogInput.confirmationText | translate}}</button>
|
||||
<button (click)="deny()" color="primary" mat-flat-button>{{confirmationDialogInput.denyText | translate}}</button>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button (click)="confirm()" color="primary" mat-flat-button>
|
||||
{{ confirmationDialogInput.confirmationText | translate }}
|
||||
</button>
|
||||
<button (click)="deny()" color="primary" mat-flat-button>
|
||||
{{ confirmationDialogInput.denyText | translate }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -1,56 +1,54 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
|
||||
export class ConfirmationDialogInput {
|
||||
public title?: string;
|
||||
public question?: string;
|
||||
public confirmationText?: string;
|
||||
public denyText?: string;
|
||||
|
||||
public title?: string;
|
||||
public question?: string;
|
||||
public confirmationText?: string;
|
||||
public denyText?: string;
|
||||
constructor(options: ConfirmationDialogInput) {
|
||||
this.title = options.title || ConfirmationDialogInput.default().title;
|
||||
this.question = options.question || ConfirmationDialogInput.default().question;
|
||||
this.confirmationText =
|
||||
options.confirmationText || ConfirmationDialogInput.default().confirmationText;
|
||||
this.denyText = options.denyText || ConfirmationDialogInput.default().denyText;
|
||||
}
|
||||
|
||||
constructor(options: ConfirmationDialogInput) {
|
||||
this.title = options.title || ConfirmationDialogInput.default().title;
|
||||
this.question = options.question || ConfirmationDialogInput.default().question;
|
||||
this.confirmationText = options.confirmationText || ConfirmationDialogInput.default().confirmationText;
|
||||
this.denyText = options.denyText || ConfirmationDialogInput.default().denyText;
|
||||
}
|
||||
|
||||
static default() {
|
||||
return new ConfirmationDialogInput({
|
||||
title: 'common.confirmation-dialog.title.label',
|
||||
question: 'common.confirmation-dialog.description.label',
|
||||
confirmationText: 'common.confirmation-dialog.confirm.label',
|
||||
denyText: 'common.confirmation-dialog.deny.label'
|
||||
});
|
||||
}
|
||||
static default() {
|
||||
return new ConfirmationDialogInput({
|
||||
title: 'common.confirmation-dialog.title.label',
|
||||
question: 'common.confirmation-dialog.description.label',
|
||||
confirmationText: 'common.confirmation-dialog.confirm.label',
|
||||
denyText: 'common.confirmation-dialog.deny.label'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-confirmation-dialog',
|
||||
templateUrl: './confirmation-dialog.component.html',
|
||||
styleUrls: ['./confirmation-dialog.component.scss']
|
||||
selector: 'redaction-confirmation-dialog',
|
||||
templateUrl: './confirmation-dialog.component.html',
|
||||
styleUrls: ['./confirmation-dialog.component.scss']
|
||||
})
|
||||
export class ConfirmationDialogComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private readonly _translateService: TranslateService,
|
||||
public dialogRef: MatDialogRef<ConfirmationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public confirmationDialogInput: ConfirmationDialogInput) {
|
||||
if (!confirmationDialogInput) {
|
||||
this.confirmationDialogInput = ConfirmationDialogInput.default();
|
||||
constructor(
|
||||
private readonly _translateService: TranslateService,
|
||||
public dialogRef: MatDialogRef<ConfirmationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public confirmationDialogInput: ConfirmationDialogInput
|
||||
) {
|
||||
if (!confirmationDialogInput) {
|
||||
this.confirmationDialogInput = ConfirmationDialogInput.default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
deny() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
deny() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
confirm() {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
.flex-row {
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<div [ngClass]="type" class="icon">
|
||||
<span>{{ type[0] }}</span>
|
||||
<span>{{ type[0] }}</span>
|
||||
</div>
|
||||
|
||||
@ -1,68 +1,72 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
.icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: $white;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 9px solid transparent;
|
||||
border-bottom-color: $grey-1;
|
||||
position: relative;
|
||||
top: -9px;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -9px;
|
||||
top: 9px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 9px solid transparent;
|
||||
border-top-color: $grey-1;
|
||||
}
|
||||
border-bottom-color: $grey-1;
|
||||
position: relative;
|
||||
top: -9px;
|
||||
|
||||
span {
|
||||
transform: translateY(9px);
|
||||
z-index: 2;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -9px;
|
||||
top: 9px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border: 9px solid transparent;
|
||||
border-top-color: $grey-1;
|
||||
}
|
||||
|
||||
span {
|
||||
transform: translateY(9px);
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.hint, .comment, .ignore {
|
||||
border-radius: 50%;
|
||||
.hint,
|
||||
.comment,
|
||||
.ignore {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.hint, .redaction, .comment {
|
||||
background-color: $grey-1;
|
||||
.hint,
|
||||
.redaction,
|
||||
.comment {
|
||||
background-color: $grey-1;
|
||||
}
|
||||
|
||||
.request{
|
||||
background-color: $blue-1;
|
||||
.request {
|
||||
background-color: $blue-1;
|
||||
}
|
||||
|
||||
.ignore {
|
||||
background-color: $grey-5;
|
||||
background-color: $grey-5;
|
||||
}
|
||||
|
||||
.hint_only {
|
||||
background-color: $orange-1;
|
||||
background-color: $orange-1;
|
||||
}
|
||||
|
||||
.vertebrate {
|
||||
background-color: $green-1;
|
||||
background-color: $green-1;
|
||||
}
|
||||
|
||||
.names {
|
||||
background-color: $yellow-2;
|
||||
background-color: $yellow-2;
|
||||
}
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-icon',
|
||||
templateUrl: './annotation-icon.component.html',
|
||||
styleUrls: ['./annotation-icon.component.scss']
|
||||
selector: 'redaction-annotation-icon',
|
||||
templateUrl: './annotation-icon.component.html',
|
||||
styleUrls: ['./annotation-icon.component.scss']
|
||||
})
|
||||
export class AnnotationIconComponent implements OnInit {
|
||||
@Input() public type: 'hint' | 'redaction' | 'suggestion' | 'ignore' | 'comment' | 'request';
|
||||
@Input() public type: 'hint' | 'redaction' | 'suggestion' | 'ignore' | 'comment' | 'request';
|
||||
|
||||
constructor() {
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
||||
@ -1,30 +1,48 @@
|
||||
<div [class]="'container flex ' + direction">
|
||||
<svg attr.height="{{size}}" attr.width="{{size}}" attr.viewBox="0 0 {{size}} {{size}}" class="donut-chart">
|
||||
<g *ngFor="let value of parsedConfig; let i = index">
|
||||
<circle attr.cx="{{cx}}"
|
||||
attr.cy="{{cy}}"
|
||||
attr.r="{{radius}}"
|
||||
[class]="value.color"
|
||||
attr.stroke-width="{{strokeWidth}}"
|
||||
attr.stroke-dasharray="{{circumference}}"
|
||||
attr.stroke-dashoffset="{{calculateStrokeDashOffset(value.value)}}"
|
||||
fill="transparent"
|
||||
attr.transform="{{returnCircleTransformValue(i)}}" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg
|
||||
attr.height="{{ size }}"
|
||||
attr.width="{{ size }}"
|
||||
attr.viewBox="0 0 {{ size }} {{ size }}"
|
||||
class="donut-chart"
|
||||
>
|
||||
<g *ngFor="let value of parsedConfig; let i = index">
|
||||
<circle
|
||||
attr.cx="{{ cx }}"
|
||||
attr.cy="{{ cy }}"
|
||||
attr.r="{{ radius }}"
|
||||
[class]="value.color"
|
||||
attr.stroke-width="{{ strokeWidth }}"
|
||||
attr.stroke-dasharray="{{ circumference }}"
|
||||
attr.stroke-dashoffset="{{ calculateStrokeDashOffset(value.value) }}"
|
||||
fill="transparent"
|
||||
attr.transform="{{ returnCircleTransformValue(i) }}"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div class="text-container" [style]="'height: ' + size + 'px; width: ' + size + 'px; padding: ' + strokeWidth + 'px;'">
|
||||
<div class="heading-xl">{{ dataTotal }}</div>
|
||||
<div class="projects-text mt-5">{{ subtitle | translate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-container">
|
||||
<div>
|
||||
<div *ngFor="let val of parsedConfig">
|
||||
<redaction-status-bar [small]="true"
|
||||
[config]="[{ length: val.value, color: val.color, label: val.value + ' ' + (val.label | translate | lowercase) }]">
|
||||
</redaction-status-bar>
|
||||
</div>
|
||||
<div
|
||||
class="text-container"
|
||||
[style]="'height: ' + size + 'px; width: ' + size + 'px; padding: ' + strokeWidth + 'px;'"
|
||||
>
|
||||
<div class="heading-xl">{{ dataTotal }}</div>
|
||||
<div class="projects-text mt-5">{{ subtitle | translate }}</div>
|
||||
</div>
|
||||
|
||||
<div class="breakdown-container">
|
||||
<div>
|
||||
<div *ngFor="let val of parsedConfig">
|
||||
<redaction-status-bar
|
||||
[small]="true"
|
||||
[config]="[
|
||||
{
|
||||
length: val.value,
|
||||
color: val.color,
|
||||
label: val.value + ' ' + (val.label | translate | lowercase)
|
||||
}
|
||||
]"
|
||||
>
|
||||
</redaction-status-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,40 +1,39 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
|
||||
&.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
&.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.text-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.breakdown-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
div {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// 'UNPROCESSED'
|
||||
// | 'REPROCESS'
|
||||
@ -46,43 +45,43 @@
|
||||
// | 'APPROVED';
|
||||
|
||||
circle {
|
||||
&.UNASSIGNED {
|
||||
stroke: $grey-5;
|
||||
}
|
||||
&.UNASSIGNED {
|
||||
stroke: $grey-5;
|
||||
}
|
||||
|
||||
&.UNPROCESSED {
|
||||
stroke: $grey-3;
|
||||
}
|
||||
&.UNPROCESSED {
|
||||
stroke: $grey-3;
|
||||
}
|
||||
|
||||
&.UNDER_REVIEW {
|
||||
stroke: $yellow-1;
|
||||
}
|
||||
&.UNDER_REVIEW {
|
||||
stroke: $yellow-1;
|
||||
}
|
||||
|
||||
&.UNDER_APPROVAL {
|
||||
stroke: $blue-4;
|
||||
}
|
||||
&.UNDER_APPROVAL {
|
||||
stroke: $blue-4;
|
||||
}
|
||||
|
||||
&.APPROVED {
|
||||
stroke: $blue-3;
|
||||
}
|
||||
&.APPROVED {
|
||||
stroke: $blue-3;
|
||||
}
|
||||
|
||||
&.PROCESSING {
|
||||
stroke: $green-2
|
||||
}
|
||||
&.PROCESSING {
|
||||
stroke: $green-2;
|
||||
}
|
||||
|
||||
&.REPROCESS {
|
||||
stroke: $green-1
|
||||
}
|
||||
&.REPROCESS {
|
||||
stroke: $green-1;
|
||||
}
|
||||
|
||||
&.ERROR {
|
||||
stroke: $red-1;
|
||||
}
|
||||
&.ERROR {
|
||||
stroke: $red-1;
|
||||
}
|
||||
|
||||
&.ACTIVE {
|
||||
stroke: $primary;
|
||||
}
|
||||
&.ACTIVE {
|
||||
stroke: $primary;
|
||||
}
|
||||
|
||||
&.ARCHIVED {
|
||||
stroke: rgba($red-1, 0.1);
|
||||
}
|
||||
&.ARCHIVED {
|
||||
stroke: rgba($red-1, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,79 +1,75 @@
|
||||
import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
|
||||
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { Color } from '../../utils/types';
|
||||
|
||||
export class DoughnutChartConfig {
|
||||
value: number;
|
||||
color: Color;
|
||||
label: string;
|
||||
value: number;
|
||||
color: Color;
|
||||
label: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-simple-doughnut-chart',
|
||||
templateUrl: './simple-doughnut-chart.component.html',
|
||||
styleUrls: ['./simple-doughnut-chart.component.scss']
|
||||
selector: 'redaction-simple-doughnut-chart',
|
||||
templateUrl: './simple-doughnut-chart.component.html',
|
||||
styleUrls: ['./simple-doughnut-chart.component.scss']
|
||||
})
|
||||
export class SimpleDoughnutChartComponent implements OnChanges {
|
||||
@Input() subtitle: string;
|
||||
@Input() config: DoughnutChartConfig[] = [];
|
||||
@Input() radius = 85;
|
||||
@Input() strokeWidth = 20;
|
||||
@Input() direction: 'row' | 'column' = 'column';
|
||||
|
||||
@Input() subtitle: string;
|
||||
@Input() config: DoughnutChartConfig[] = [];
|
||||
@Input() radius = 85;
|
||||
@Input() strokeWidth = 20;
|
||||
@Input() direction: 'row' | 'column' = 'column';
|
||||
public chartData: any[] = [];
|
||||
public perimeter: number;
|
||||
public cx = 0;
|
||||
public cy = 0;
|
||||
public size = 0;
|
||||
|
||||
public chartData: any[] = [];
|
||||
public perimeter: number;
|
||||
public cx = 0;
|
||||
public cy = 0;
|
||||
public size = 0;
|
||||
constructor() {}
|
||||
|
||||
constructor() {
|
||||
}
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.calculateChartData();
|
||||
this.cx = this.radius + this.strokeWidth / 2;
|
||||
this.cy = this.radius + this.strokeWidth / 2;
|
||||
this.size = this.strokeWidth + this.radius * 2;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.calculateChartData();
|
||||
this.cx = this.radius + (this.strokeWidth / 2);
|
||||
this.cy = this.radius + (this.strokeWidth / 2);
|
||||
this.size = this.strokeWidth+(this.radius * 2) ;
|
||||
}
|
||||
get circumference() {
|
||||
return 2 * Math.PI * this.radius;
|
||||
}
|
||||
|
||||
get circumference() {
|
||||
return 2 * Math.PI * this.radius;
|
||||
};
|
||||
get dataTotal() {
|
||||
return this.config.map((v) => v.value).reduce((acc, val) => acc + val, 0);
|
||||
}
|
||||
|
||||
get dataTotal() {
|
||||
return this.config.map(v => v.value).reduce((acc, val) => acc + val, 0);
|
||||
};
|
||||
calculateChartData() {
|
||||
const newData = [];
|
||||
let angleOffset = -90;
|
||||
this.config.forEach((dataVal) => {
|
||||
const data = {
|
||||
degrees: angleOffset
|
||||
};
|
||||
newData.push(data);
|
||||
angleOffset = this.dataPercentage(dataVal.value) * 360 + angleOffset;
|
||||
});
|
||||
this.chartData = newData;
|
||||
}
|
||||
|
||||
calculateChartData() {
|
||||
const newData = [];
|
||||
let angleOffset = -90;
|
||||
this.config.forEach((dataVal) => {
|
||||
const data = {
|
||||
degrees: angleOffset,
|
||||
}
|
||||
newData.push(data)
|
||||
angleOffset = this.dataPercentage(dataVal.value) * 360 + angleOffset;
|
||||
})
|
||||
this.chartData = newData;
|
||||
}
|
||||
calculateStrokeDashOffset(dataVal) {
|
||||
const strokeDiff = this.dataPercentage(dataVal) * this.circumference;
|
||||
return this.circumference - strokeDiff;
|
||||
}
|
||||
|
||||
calculateStrokeDashOffset(dataVal) {
|
||||
const strokeDiff = this.dataPercentage(dataVal) * this.circumference
|
||||
return this.circumference - strokeDiff;
|
||||
}
|
||||
|
||||
dataPercentage(dataVal) {
|
||||
return dataVal / this.dataTotal;
|
||||
}
|
||||
|
||||
returnCircleTransformValue(index) {
|
||||
return `rotate(${this.chartData[index].degrees}, ${this.cx}, ${this.cy})`;
|
||||
}
|
||||
|
||||
// Eliminate items with value = 0
|
||||
public get parsedConfig() {
|
||||
return this.config.filter((el) => el.value);
|
||||
}
|
||||
dataPercentage(dataVal) {
|
||||
return dataVal / this.dataTotal;
|
||||
}
|
||||
|
||||
returnCircleTransformValue(index) {
|
||||
return `rotate(${this.chartData[index].degrees}, ${this.cx}, ${this.cy})`;
|
||||
}
|
||||
|
||||
// Eliminate items with value = 0
|
||||
public get parsedConfig() {
|
||||
return this.config.filter((el) => el.value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,28 +2,26 @@ import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { Color } from '../../utils/types';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-status-bar',
|
||||
templateUrl: './status-bar.component.html',
|
||||
styleUrls: ['./status-bar.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
selector: 'redaction-status-bar',
|
||||
templateUrl: './status-bar.component.html',
|
||||
styleUrls: ['./status-bar.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None
|
||||
})
|
||||
export class StatusBarComponent implements OnInit {
|
||||
@Input()
|
||||
public config: {
|
||||
length: number,
|
||||
color: Color,
|
||||
label?: string,
|
||||
}[] = [];
|
||||
@Input()
|
||||
public config: {
|
||||
length: number;
|
||||
color: Color;
|
||||
label?: string;
|
||||
}[] = [];
|
||||
|
||||
@Input()
|
||||
public small = false;
|
||||
@Input()
|
||||
public small = false;
|
||||
|
||||
@Input()
|
||||
public labelClass = '';
|
||||
@Input()
|
||||
public labelClass = '';
|
||||
|
||||
constructor() {
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
||||
@ -1,46 +1,44 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {Project} from "@redaction/red-ui-http";
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
import {AppStateService} from "../../state/app-state.service";
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { Project } from '@redaction/red-ui-http';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-add-edit-project-dialog',
|
||||
templateUrl: './add-edit-project-dialog.component.html',
|
||||
styleUrls: ['./add-edit-project-dialog.component.scss']
|
||||
selector: 'redaction-add-edit-project-dialog',
|
||||
templateUrl: './add-edit-project-dialog.component.html',
|
||||
styleUrls: ['./add-edit-project-dialog.component.scss']
|
||||
})
|
||||
export class AddEditProjectDialogComponent implements OnInit {
|
||||
projectForm: FormGroup;
|
||||
|
||||
projectForm: FormGroup;
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
public dialogRef: MatDialogRef<AddEditProjectDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public project: Project
|
||||
) {}
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
public dialogRef: MatDialogRef<AddEditProjectDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public project: Project) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectForm = this._formBuilder.group({
|
||||
projectName: [this.project?.projectName, Validators.required],
|
||||
description: [this.project?.description],
|
||||
dueDate: [this.project?.dueDate]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async saveProject() {
|
||||
const project: Project = this._formToObject();
|
||||
project.projectId = this.project?.projectId;
|
||||
await this._appStateService.addOrUpdateProject(project);
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
private _formToObject(): Project {
|
||||
return {
|
||||
projectName: this.projectForm.get('projectName').value,
|
||||
description: this.projectForm.get('description').value,
|
||||
dueDate: this.projectForm.get('dueDate').value
|
||||
ngOnInit(): void {
|
||||
this.projectForm = this._formBuilder.group({
|
||||
projectName: [this.project?.projectName, Validators.required],
|
||||
description: [this.project?.description],
|
||||
dueDate: [this.project?.dueDate]
|
||||
});
|
||||
}
|
||||
|
||||
async saveProject() {
|
||||
const project: Project = this._formToObject();
|
||||
project.projectId = this.project?.projectId;
|
||||
await this._appStateService.addOrUpdateProject(project);
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
private _formToObject(): Project {
|
||||
return {
|
||||
projectName: this.projectForm.get('projectName').value,
|
||||
description: this.projectForm.get('description').value,
|
||||
dueDate: this.projectForm.get('dueDate').value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,41 +1,48 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="'assign-' + data.type + '-owner.dialog.title.label'"
|
||||
class="dialog-header heading-l">
|
||||
</div>
|
||||
<div
|
||||
[translate]="'assign-' + data.type + '-owner.dialog.title.label'"
|
||||
class="dialog-header heading-l"
|
||||
></div>
|
||||
|
||||
<form (submit)="saveUsers()" [formGroup]="usersForm">
|
||||
<div class="dialog-content">
|
||||
<form (submit)="saveUsers()" [formGroup]="usersForm">
|
||||
<div class="dialog-content">
|
||||
<div class="red-input-group">
|
||||
<mat-form-field>
|
||||
<mat-label>{{
|
||||
'assign-' + data.type + '-owner.dialog.single-user.label' | translate
|
||||
}}</mat-label>
|
||||
<mat-select formControlName="singleUser">
|
||||
<mat-option
|
||||
*ngFor="let userId of singleUsersSelectOptions"
|
||||
[value]="userId"
|
||||
>
|
||||
{{ userService.getNameForId(userId) }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="red-input-group">
|
||||
<mat-form-field *ngIf="data.type === 'project'">
|
||||
<mat-label>{{
|
||||
'assign-' + data.type + '-owner.dialog.multi-user.label' | translate
|
||||
}}</mat-label>
|
||||
<mat-select formControlName="userList" multiple="true">
|
||||
<mat-option *ngFor="let userId of multiUsersSelectOptions" [value]="userId">
|
||||
{{ userService.getNameForId(userId) }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="red-input-group">
|
||||
<mat-form-field>
|
||||
<mat-label>{{'assign-' + data.type + '-owner.dialog.single-user.label' | translate}}</mat-label>
|
||||
<mat-select formControlName="singleUser">
|
||||
<mat-option *ngFor="let userId of singleUsersSelectOptions" [value]="userId">
|
||||
{{userService.getNameForId(userId)}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="red-input-group">
|
||||
<mat-form-field *ngIf="data.type === 'project' ">
|
||||
<mat-label>{{'assign-' + data.type + '-owner.dialog.multi-user.label' | translate}}</mat-label>
|
||||
<mat-select formControlName="userList" multiple="true">
|
||||
<mat-option *ngFor="let userId of multiUsersSelectOptions" [value]="userId">
|
||||
{{userService.getNameForId(userId)}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button type="submit" [disabled]="!usersForm.valid">
|
||||
{{ 'assign-' + data.type + '-owner.dialog.save.label' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button color="primary" mat-flat-button type="submit"
|
||||
[disabled]="!usersForm.valid"> {{'assign-' + data.type + '-owner.dialog.save.label' | translate}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
.red-input-group {
|
||||
max-width: 200px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
@ -1,90 +1,113 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {FileStatus, Project, ProjectControllerService, StatusControllerService} from '@redaction/red-ui-http';
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
|
||||
import {AppStateService} from '../../state/app-state.service';
|
||||
import {UserService} from '../../user/user.service';
|
||||
import {NotificationService, NotificationType} from '../../notification/notification.service';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import {
|
||||
FileStatus,
|
||||
Project,
|
||||
ProjectControllerService,
|
||||
StatusControllerService
|
||||
} from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
class DialogData {
|
||||
type: 'file' | 'project';
|
||||
project?: Project;
|
||||
file?: FileStatus;
|
||||
type: 'file' | 'project';
|
||||
project?: Project;
|
||||
file?: FileStatus;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './assign-owner-dialog.component.html',
|
||||
styleUrls: ['./assign-owner-dialog.component.scss']
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './assign-owner-dialog.component.html',
|
||||
styleUrls: ['./assign-owner-dialog.component.scss']
|
||||
})
|
||||
export class AssignOwnerDialogComponent {
|
||||
usersForm: FormGroup;
|
||||
|
||||
usersForm: FormGroup;
|
||||
|
||||
constructor(public readonly userService: UserService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public dialogRef: MatDialogRef<AssignOwnerDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
|
||||
private _loadData() {
|
||||
if (this.data.type === 'project') {
|
||||
const project = this.data.project;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [project?.ownerId, Validators.required],
|
||||
userList: [project?.memberIds]
|
||||
});
|
||||
constructor(
|
||||
public readonly userService: UserService,
|
||||
private readonly _projectControllerService: ProjectControllerService,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _statusControllerService: StatusControllerService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
public dialogRef: MatDialogRef<AssignOwnerDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: DialogData
|
||||
) {
|
||||
this._loadData();
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
const file = this.data.file;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [file?.currentReviewer]
|
||||
});
|
||||
}
|
||||
}
|
||||
private _loadData() {
|
||||
if (this.data.type === 'project') {
|
||||
const project = this.data.project;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [project?.ownerId, Validators.required],
|
||||
userList: [project?.memberIds]
|
||||
});
|
||||
}
|
||||
|
||||
async saveUsers() {
|
||||
try {
|
||||
if (this.data.type === 'project') {
|
||||
|
||||
const ownerId = this.usersForm.get('singleUser').value;
|
||||
const memberIds = this.usersForm.get('userList').value;
|
||||
const project = Object.assign({},this.data.project);
|
||||
project.memberIds = memberIds;
|
||||
project.ownerId = ownerId;
|
||||
await this._appStateService.addOrUpdateProject(project);
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(ownerId) + ' to project: ' + project.projectName);
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
|
||||
const reviewerId = this.usersForm.get('singleUser').value;
|
||||
|
||||
await this._statusControllerService.assignProjectOwner1(this._appStateService.activeProjectId, this.data.file.fileId, reviewerId).toPromise();
|
||||
this.data.file.currentReviewer = reviewerId;
|
||||
this._notificationService.showToastNotification('Successfully assigned ' + this.userService.getNameForId(reviewerId) + ' to file: ' + this.data.file.filename);
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
this._notificationService.showToastNotification('Failed: ' + error.error.message, null, NotificationType.ERROR);
|
||||
if (this.data.type === 'file') {
|
||||
const file = this.data.file;
|
||||
this.usersForm = this._formBuilder.group({
|
||||
singleUser: [file?.currentReviewer]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.dialogRef.close();
|
||||
}
|
||||
async saveUsers() {
|
||||
try {
|
||||
if (this.data.type === 'project') {
|
||||
const ownerId = this.usersForm.get('singleUser').value;
|
||||
const memberIds = this.usersForm.get('userList').value;
|
||||
const project = Object.assign({}, this.data.project);
|
||||
project.memberIds = memberIds;
|
||||
project.ownerId = ownerId;
|
||||
await this._appStateService.addOrUpdateProject(project);
|
||||
this._notificationService.showToastNotification(
|
||||
'Successfully assigned ' +
|
||||
this.userService.getNameForId(ownerId) +
|
||||
' to project: ' +
|
||||
project.projectName
|
||||
);
|
||||
}
|
||||
|
||||
if (this.data.type === 'file') {
|
||||
const reviewerId = this.usersForm.get('singleUser').value;
|
||||
|
||||
get singleUsersSelectOptions() {
|
||||
return this.data.type === 'file' ? this._appStateService.activeProject.project.memberIds : this.userService.managerUsers.map(m => m.userId);
|
||||
}
|
||||
await this._statusControllerService
|
||||
.assignProjectOwner1(
|
||||
this._appStateService.activeProjectId,
|
||||
this.data.file.fileId,
|
||||
reviewerId
|
||||
)
|
||||
.toPromise();
|
||||
this.data.file.currentReviewer = reviewerId;
|
||||
this._notificationService.showToastNotification(
|
||||
'Successfully assigned ' +
|
||||
this.userService.getNameForId(reviewerId) +
|
||||
' to file: ' +
|
||||
this.data.file.filename
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this._notificationService.showToastNotification(
|
||||
'Failed: ' + error.error.message,
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
|
||||
get multiUsersSelectOptions() {
|
||||
return this.userService.managerUsers.map(m => m.userId);
|
||||
}
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
get singleUsersSelectOptions() {
|
||||
return this.data.type === 'file'
|
||||
? this._appStateService.activeProject.project.memberIds
|
||||
: this.userService.managerUsers.map((m) => m.userId);
|
||||
}
|
||||
|
||||
get multiUsersSelectOptions() {
|
||||
return this.userService.managerUsers.map((m) => m.userId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,40 +1,53 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="'file-details.dialog.title.label'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div [translate]="'file-details.dialog.title.label'"
|
||||
class="dialog-header heading-l">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="file-details">
|
||||
<div class="detail-row">
|
||||
{{fileStatus.filename}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{'project-overview.file-listing.file-entry.status.label'| translate:fileStatus}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{'project-overview.file-listing.file-entry.number-of-pages.label'| translate:fileStatus}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{'project-overview.file-listing.file-entry.number-of-analyses.label'| translate:fileStatus}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{'project-overview.file-listing.file-entry.added.label'| translate:{added: fileStatus.added | date:'short'} }}
|
||||
</div>
|
||||
<div *ngIf="fileStatus.lastUpdated" class="detail-row">
|
||||
{{'project-overview.file-listing.file-entry.last-updated.label'| translate:{lastUpdated: fileStatus.lastUpdated | date:'short'} }}
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<div class="file-details">
|
||||
<div class="detail-row">
|
||||
{{ fileStatus.filename }}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{
|
||||
'project-overview.file-listing.file-entry.status.label' | translate: fileStatus
|
||||
}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{
|
||||
'project-overview.file-listing.file-entry.number-of-pages.label'
|
||||
| translate: fileStatus
|
||||
}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{
|
||||
'project-overview.file-listing.file-entry.number-of-analyses.label'
|
||||
| translate: fileStatus
|
||||
}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{
|
||||
'project-overview.file-listing.file-entry.added.label'
|
||||
| translate: { added: fileStatus.added | date: 'short' }
|
||||
}}
|
||||
</div>
|
||||
<div *ngIf="fileStatus.lastUpdated" class="detail-row">
|
||||
{{
|
||||
'project-overview.file-listing.file-entry.last-updated.label'
|
||||
| translate: { lastUpdated: fileStatus.lastUpdated | date: 'short' }
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button (click)="downloadRedactionReport()" color="primary" mat-flat-button
|
||||
translate="file-details.dialog.actions.download-redaction-report.label"></button>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button
|
||||
(click)="downloadRedactionReport()"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
translate="file-details.dialog.actions.download-redaction-report.label"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -1,2 +1 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
@ -1,29 +1,27 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FileStatus, FileUploadControllerService} from "@redaction/red-ui-http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {download} from "../../utils/file-download-utils";
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FileStatus, FileUploadControllerService } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { download } from '../../utils/file-download-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-details-dialog',
|
||||
templateUrl: './file-details-dialog.component.html',
|
||||
styleUrls: ['./file-details-dialog.component.scss']
|
||||
selector: 'redaction-file-details-dialog',
|
||||
templateUrl: './file-details-dialog.component.html',
|
||||
styleUrls: ['./file-details-dialog.component.scss']
|
||||
})
|
||||
export class FileDetailsDialogComponent implements OnInit {
|
||||
constructor(
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
public dialogRef: MatDialogRef<FileDetailsDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public fileStatus: FileStatus
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
constructor(
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
public dialogRef: MatDialogRef<FileDetailsDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public fileStatus: FileStatus) {
|
||||
}
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
downloadRedactionReport() {
|
||||
this._fileUploadControllerService.downloadRedactionReport({fileIds: [this.fileStatus.fileId]}, true, 'response').subscribe(data => {
|
||||
download(data, 'redaction-report-' + this.fileStatus.filename + ".docx");
|
||||
});
|
||||
}
|
||||
downloadRedactionReport() {
|
||||
this._fileUploadControllerService
|
||||
.downloadRedactionReport({ fileIds: [this.fileStatus.fileId] }, true, 'response')
|
||||
.subscribe((data) => {
|
||||
download(data, 'redaction-report-' + this.fileStatus.filename + '.docx');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,94 +1,139 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
|
||||
import {AppStateService} from "../../state/app-state.service";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import {
|
||||
AddRedactionRequest,
|
||||
DictionaryControllerService,
|
||||
ManualRedactionControllerService,
|
||||
TypeValue
|
||||
} from "@redaction/red-ui-http";
|
||||
import {NotificationService, NotificationType} from "../../notification/notification.service";
|
||||
import {TranslateService} from "@ngx-translate/core";
|
||||
import {map} from "rxjs/operators";
|
||||
import {Observable} from "rxjs";
|
||||
import {UserService} from "../../user/user.service";
|
||||
|
||||
AddRedactionRequest,
|
||||
DictionaryControllerService,
|
||||
ManualRedactionControllerService,
|
||||
TypeValue
|
||||
} from '@redaction/red-ui-http';
|
||||
import { NotificationService, NotificationType } from '../../notification/notification.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { UserService } from '../../user/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-manual-redaction-dialog',
|
||||
templateUrl: './manual-redaction-dialog.component.html',
|
||||
styleUrls: ['./manual-redaction-dialog.component.scss']
|
||||
selector: 'redaction-manual-redaction-dialog',
|
||||
templateUrl: './manual-redaction-dialog.component.html',
|
||||
styleUrls: ['./manual-redaction-dialog.component.scss']
|
||||
})
|
||||
export class ManualRedactionDialogComponent implements OnInit {
|
||||
redactionForm: FormGroup;
|
||||
dictionaries: Observable<Array<TypeValue>>;
|
||||
isDocumentAdmin: boolean;
|
||||
|
||||
redactionForm: FormGroup;
|
||||
dictionaries: Observable<Array<TypeValue>>;
|
||||
isDocumentAdmin: boolean;
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
public dialogRef: MatDialogRef<ManualRedactionDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public addRedactionRequest: AddRedactionRequest
|
||||
) {}
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _notificationService: NotificationService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||
private readonly _dictionaryControllerService: DictionaryControllerService,
|
||||
public dialogRef: MatDialogRef<ManualRedactionDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public addRedactionRequest: AddRedactionRequest) {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.isDocumentAdmin = (this._appStateService.isActiveProjectOwner || this._appStateService.isActiveFileDocumentReviewer) && this._userService.user.isManager;
|
||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||
this.redactionForm = this._formBuilder.group({
|
||||
addToDictionary: [false],
|
||||
reason: [null, Validators.required],
|
||||
dictionary: [null, Validators.required],
|
||||
comment: commentField,
|
||||
});
|
||||
this.dictionaries = this._dictionaryControllerService.getAllTypes().pipe(map(r => r.types));
|
||||
}
|
||||
|
||||
|
||||
handleAddRedaction() {
|
||||
if (this.isDocumentAdmin) {
|
||||
this.addManualRedaction();
|
||||
} else {
|
||||
this.suggestManualRedaction()
|
||||
async ngOnInit() {
|
||||
this.isDocumentAdmin =
|
||||
(this._appStateService.isActiveProjectOwner ||
|
||||
this._appStateService.isActiveFileDocumentReviewer) &&
|
||||
this._userService.user.isManager;
|
||||
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
|
||||
this.redactionForm = this._formBuilder.group({
|
||||
addToDictionary: [false],
|
||||
reason: [null, Validators.required],
|
||||
dictionary: [null, Validators.required],
|
||||
comment: commentField
|
||||
});
|
||||
this.dictionaries = this._dictionaryControllerService
|
||||
.getAllTypes()
|
||||
.pipe(map((r) => r.types));
|
||||
}
|
||||
}
|
||||
|
||||
suggestManualRedaction() {
|
||||
const mre = Object.assign({}, this.addRedactionRequest);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService.requestAddRedaction(mre, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId).subscribe(ok => {
|
||||
this._appStateService.reanalyseActiveFile();
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.dialog.add-redaction.success.label'), null, NotificationType.SUCCESS);
|
||||
this.dialogRef.close();
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.dialog.add-redaction.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
handleAddRedaction() {
|
||||
if (this.isDocumentAdmin) {
|
||||
this.addManualRedaction();
|
||||
} else {
|
||||
this.suggestManualRedaction();
|
||||
}
|
||||
}
|
||||
|
||||
addManualRedaction() {
|
||||
const mre = Object.assign({}, this.addRedactionRequest);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService.addRedaction(mre, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId).subscribe(ok => {
|
||||
this._appStateService.reanalyseActiveFile();
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.dialog.add-redaction.success.label'), null, NotificationType.SUCCESS);
|
||||
this.dialogRef.close();
|
||||
}, (err) => {
|
||||
this._notificationService.showToastNotification(this._translateService.instant('manual-redaction.dialog.add-redaction.failed.label', err), null, NotificationType.ERROR);
|
||||
});
|
||||
}
|
||||
suggestManualRedaction() {
|
||||
const mre = Object.assign({}, this.addRedactionRequest);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService
|
||||
.requestAddRedaction(
|
||||
mre,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.subscribe(
|
||||
(ok) => {
|
||||
this._appStateService.reanalyseActiveFile();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
this.dialogRef.close();
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
|
||||
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
|
||||
addRedactionRequest.addToDictionary = this.redactionForm.get('addToDictionary').value;
|
||||
addRedactionRequest.reason = this.redactionForm.get('reason').value;
|
||||
const commentValue = this.redactionForm.get('comment').value;
|
||||
addRedactionRequest.comment = commentValue ? {text: commentValue} : null;
|
||||
}
|
||||
addManualRedaction() {
|
||||
const mre = Object.assign({}, this.addRedactionRequest);
|
||||
this._enhanceManualRedaction(mre);
|
||||
this._manualRedactionControllerService
|
||||
.addRedaction(
|
||||
mre,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
this._appStateService.activeFile.fileId
|
||||
)
|
||||
.subscribe(
|
||||
(ok) => {
|
||||
this._appStateService.reanalyseActiveFile();
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.success.label'
|
||||
),
|
||||
null,
|
||||
NotificationType.SUCCESS
|
||||
);
|
||||
this.dialogRef.close();
|
||||
},
|
||||
(err) => {
|
||||
this._notificationService.showToastNotification(
|
||||
this._translateService.instant(
|
||||
'manual-redaction.dialog.add-redaction.failed.label',
|
||||
err
|
||||
),
|
||||
null,
|
||||
NotificationType.ERROR
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
|
||||
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
|
||||
addRedactionRequest.addToDictionary = this.redactionForm.get('addToDictionary').value;
|
||||
addRedactionRequest.reason = this.redactionForm.get('reason').value;
|
||||
const commentValue = this.redactionForm.get('comment').value;
|
||||
addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,36 +1,43 @@
|
||||
<section class="dialog">
|
||||
<div [translate]="'project-details.dialog.title.label'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div [translate]="'project-details.dialog.title.label'"
|
||||
class="dialog-header heading-l">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="file-details">
|
||||
<div class="detail-row">
|
||||
{{projectDetails.project.projectName}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{projectDetails.project.description}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{projectDetails.project.date | date:'short'}}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{'project-details.dialog.info.file-count.label'| translate:{fileCount: projectDetails.files ? projectDetails.files.length : 0} }}
|
||||
</div>
|
||||
<div class="dialog-content">
|
||||
<div class="file-details">
|
||||
<div class="detail-row">
|
||||
{{ projectDetails.project.projectName }}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{ projectDetails.project.description }}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{ projectDetails.project.date | date: 'short' }}
|
||||
</div>
|
||||
<div class="detail-row">
|
||||
{{
|
||||
'project-details.dialog.info.file-count.label'
|
||||
| translate
|
||||
: { fileCount: projectDetails.files ? projectDetails.files.length : 0 }
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<button (click)="downloadRedactionReport()" color="primary" mat-flat-button
|
||||
translate="project-details.dialog.actions.download-redaction-report.label"></button>
|
||||
<button (click)="reanalyseProject()" color="primary" mat-flat-button
|
||||
translate="project-details.dialog.actions.reanalyse-project.label"></button>
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<button
|
||||
(click)="downloadRedactionReport()"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
translate="project-details.dialog.actions.download-redaction-report.label"
|
||||
></button>
|
||||
<button
|
||||
(click)="reanalyseProject()"
|
||||
color="primary"
|
||||
mat-flat-button
|
||||
translate="project-details.dialog.actions.reanalyse-project.label"
|
||||
></button>
|
||||
</div>
|
||||
|
||||
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
<button (click)="dialogRef.close()" class="dialog-close" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -1,35 +1,44 @@
|
||||
import {Component, Inject, OnInit} from '@angular/core';
|
||||
import {FileUploadControllerService, ReanalysisControllerService} from "@redaction/red-ui-http";
|
||||
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
|
||||
import {download} from "../../utils/file-download-utils";
|
||||
import {ProjectWrapper} from "../../state/app-state.service";
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { FileUploadControllerService, ReanalysisControllerService } from '@redaction/red-ui-http';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { download } from '../../utils/file-download-utils';
|
||||
import { ProjectWrapper } from '../../state/app-state.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './project-details-dialog.component.html',
|
||||
styleUrls: ['./project-details-dialog.component.scss']
|
||||
selector: 'redaction-project-details-dialog',
|
||||
templateUrl: './project-details-dialog.component.html',
|
||||
styleUrls: ['./project-details-dialog.component.scss']
|
||||
})
|
||||
export class ProjectDetailsDialogComponent implements OnInit {
|
||||
constructor(
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
public dialogRef: MatDialogRef<ProjectDetailsDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public projectDetails: ProjectWrapper
|
||||
) {}
|
||||
|
||||
constructor(
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||
public dialogRef: MatDialogRef<ProjectDetailsDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public projectDetails: ProjectWrapper) {
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
downloadRedactionReport() {
|
||||
this._fileUploadControllerService
|
||||
.downloadRedactionReportForProject(
|
||||
this.projectDetails.project.projectId,
|
||||
true,
|
||||
'response'
|
||||
)
|
||||
.subscribe((data) => {
|
||||
download(
|
||||
data,
|
||||
'redaction-report-' + this.projectDetails.project.projectName + '.docx'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
downloadRedactionReport() {
|
||||
this._fileUploadControllerService.downloadRedactionReportForProject(this.projectDetails.project.projectId, true, 'response').subscribe(data => {
|
||||
download(data, 'redaction-report-' + this.projectDetails.project.projectName + ".docx");
|
||||
});
|
||||
}
|
||||
|
||||
reanalyseProject() {
|
||||
this._reanalysisControllerService.reanalyzeProject(this.projectDetails.project.projectId).subscribe(() => {
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
reanalyseProject() {
|
||||
this._reanalysisControllerService
|
||||
.reanalyzeProject(this.projectDetails.project.projectId)
|
||||
.subscribe(() => {
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {LanguageService} from "./language.service";
|
||||
import { LanguageService } from './language.service';
|
||||
|
||||
export function languageInitializer(languageService: LanguageService) {
|
||||
return () => languageService.chooseAndSetInitialLanguage();
|
||||
return () => languageService.chooseAndSetInitialLanguage();
|
||||
}
|
||||
|
||||
@ -1,43 +1,39 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {TranslateService} from '@ngx-translate/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root"
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class LanguageService {
|
||||
constructor(
|
||||
private translateService: TranslateService
|
||||
) {
|
||||
translateService.addLangs(['en', 'de']);
|
||||
}
|
||||
|
||||
get currentLanguage() {
|
||||
return this.translateService.currentLang;
|
||||
}
|
||||
|
||||
chooseAndSetInitialLanguage() {
|
||||
let defaultLang: string;
|
||||
const localStorageLang = localStorage.getItem('redaction.language');
|
||||
const browserLang = this.translateService.getBrowserLang();
|
||||
// @ts-ignore
|
||||
if (this.translateService.getLangs().includes(localStorageLang)) {
|
||||
defaultLang = localStorageLang;
|
||||
// @ts-ignore
|
||||
} else if (this.translateService.getLangs().includes(browserLang)) {
|
||||
defaultLang = browserLang;
|
||||
} else {
|
||||
defaultLang = 'en';
|
||||
constructor(private translateService: TranslateService) {
|
||||
translateService.addLangs(['en', 'de']);
|
||||
}
|
||||
document.documentElement.lang = defaultLang;
|
||||
this.translateService.setDefaultLang(defaultLang);
|
||||
this.translateService.use(defaultLang).subscribe(() => {
|
||||
});
|
||||
}
|
||||
|
||||
changeLanguage(language: string) {
|
||||
localStorage.setItem('redaction.language', language);
|
||||
document.documentElement.lang = language;
|
||||
this.translateService.use(language).subscribe(() => {
|
||||
});
|
||||
}
|
||||
get currentLanguage() {
|
||||
return this.translateService.currentLang;
|
||||
}
|
||||
|
||||
chooseAndSetInitialLanguage() {
|
||||
let defaultLang: string;
|
||||
const localStorageLang = localStorage.getItem('redaction.language');
|
||||
const browserLang = this.translateService.getBrowserLang();
|
||||
// @ts-ignore
|
||||
if (this.translateService.getLangs().includes(localStorageLang)) {
|
||||
defaultLang = localStorageLang;
|
||||
// @ts-ignore
|
||||
} else if (this.translateService.getLangs().includes(browserLang)) {
|
||||
defaultLang = browserLang;
|
||||
} else {
|
||||
defaultLang = 'en';
|
||||
}
|
||||
document.documentElement.lang = defaultLang;
|
||||
this.translateService.setDefaultLang(defaultLang);
|
||||
this.translateService.use(defaultLang).subscribe(() => {});
|
||||
}
|
||||
|
||||
changeLanguage(language: string) {
|
||||
localStorage.setItem('redaction.language', language);
|
||||
document.documentElement.lang = language;
|
||||
this.translateService.use(language).subscribe(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {AppConfigService} from "../app-config/app-config.service";
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
|
||||
@Injectable()
|
||||
export class ApiPathInterceptorService implements HttpInterceptor {
|
||||
constructor(private readonly _appConfigService: AppConfigService) {}
|
||||
|
||||
constructor(private readonly _appConfigService: AppConfigService) {
|
||||
}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req);
|
||||
}
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
return next.handle(req);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div class="redacto-logo">
|
||||
<div class="line-1"></div>
|
||||
<div class="line-2"></div>
|
||||
<div class="line-1"></div>
|
||||
<div class="line-2"></div>
|
||||
</div>
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-logo',
|
||||
templateUrl: './logo.component.html',
|
||||
styleUrls: ['./logo.component.scss']
|
||||
selector: 'redaction-logo',
|
||||
templateUrl: './logo.component.html',
|
||||
styleUrls: ['./logo.component.scss']
|
||||
})
|
||||
export class LogoComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
||||
@ -1,36 +1,38 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
export enum NotificationType {
|
||||
SUCCESS = 'SUCCESS', WARNING = 'WARNING', ERROR = 'ERROR', INFO = 'INFO',
|
||||
SUCCESS = 'SUCCESS',
|
||||
WARNING = 'WARNING',
|
||||
ERROR = 'ERROR',
|
||||
INFO = 'INFO'
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NotificationService {
|
||||
constructor(private readonly _snackBar: MatSnackBar, private readonly _toastr: ToastrService) {}
|
||||
|
||||
constructor(private readonly _snackBar: MatSnackBar,
|
||||
private readonly _toastr: ToastrService) {
|
||||
}
|
||||
|
||||
showToastNotification(message: string, title?: string, notificationType: NotificationType = NotificationType.INFO) {
|
||||
switch (notificationType) {
|
||||
case NotificationType.ERROR:
|
||||
this._toastr.error(message, title);
|
||||
break;
|
||||
case NotificationType.SUCCESS:
|
||||
this._toastr.success(message, title);
|
||||
break;
|
||||
case NotificationType.WARNING:
|
||||
this._toastr.warning(message, title);
|
||||
break
|
||||
case NotificationType.INFO:
|
||||
this._toastr.info(message, title);
|
||||
break
|
||||
showToastNotification(
|
||||
message: string,
|
||||
title?: string,
|
||||
notificationType: NotificationType = NotificationType.INFO
|
||||
) {
|
||||
switch (notificationType) {
|
||||
case NotificationType.ERROR:
|
||||
this._toastr.error(message, title);
|
||||
break;
|
||||
case NotificationType.SUCCESS:
|
||||
this._toastr.success(message, title);
|
||||
break;
|
||||
case NotificationType.WARNING:
|
||||
this._toastr.warning(message, title);
|
||||
break;
|
||||
case NotificationType.INFO:
|
||||
this._toastr.info(message, title);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,12 +1,30 @@
|
||||
<section>
|
||||
<p class="heading-xl" [translate]="'auth-error.heading.label'"
|
||||
*ngIf="!configuredAdminName && !configuredAdminUrl"></p>
|
||||
<p class="heading-xl"
|
||||
[innerHTML]="'auth-error.heading-with-name-and-link.label' | translate:{adminName: configuredAdminName, adminUrl: configuredAdminUrl}"
|
||||
*ngIf="configuredAdminName && configuredAdminUrl"></p>
|
||||
<p class="heading-xl" [innerHTML]="'auth-error.heading-with-name.label' | translate:{adminName: configuredAdminName }"
|
||||
*ngIf="configuredAdminName && !configuredAdminUrl"></p>
|
||||
<p class="heading-xl" [innerHTML]="'auth-error.heading-with-link.label' | translate:{adminName: configuredAdminName }"
|
||||
*ngIf="!configuredAdminName && configuredAdminUrl"></p>
|
||||
<a (click)="logout()" [translate]="'auth-error.logout.label'"></a>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[translate]="'auth-error.heading.label'"
|
||||
*ngIf="!configuredAdminName && !configuredAdminUrl"
|
||||
></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="
|
||||
'auth-error.heading-with-name-and-link.label'
|
||||
| translate: { adminName: configuredAdminName, adminUrl: configuredAdminUrl }
|
||||
"
|
||||
*ngIf="configuredAdminName && configuredAdminUrl"
|
||||
></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="
|
||||
'auth-error.heading-with-name.label' | translate: { adminName: configuredAdminName }
|
||||
"
|
||||
*ngIf="configuredAdminName && !configuredAdminUrl"
|
||||
></p>
|
||||
<p
|
||||
class="heading-xl"
|
||||
[innerHTML]="
|
||||
'auth-error.heading-with-link.label' | translate: { adminName: configuredAdminName }
|
||||
"
|
||||
*ngIf="!configuredAdminName && configuredAdminUrl"
|
||||
></p>
|
||||
<a (click)="logout()" [translate]="'auth-error.logout.label'"></a>
|
||||
</section>
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
section {
|
||||
padding: 24px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
@ -1,28 +1,29 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {UserService} from "../../user/user.service";
|
||||
import {AppConfigKey, AppConfigService} from "../../app-config/app-config.service";
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { AppConfigKey, AppConfigService } from '../../app-config/app-config.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-auth-error',
|
||||
templateUrl: './auth-error.component.html',
|
||||
styleUrls: ['./auth-error.component.scss']
|
||||
selector: 'redaction-auth-error',
|
||||
templateUrl: './auth-error.component.html',
|
||||
styleUrls: ['./auth-error.component.scss']
|
||||
})
|
||||
export class AuthErrorComponent implements OnInit {
|
||||
configuredAdminName: string;
|
||||
configuredAdminUrl: string;
|
||||
|
||||
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.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
this.configuredAdminName = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_NAME);
|
||||
this.configuredAdminUrl = this._appConfigService.getConfig(AppConfigKey.ADMIN_CONTACT_URL);
|
||||
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._userService.logout();
|
||||
}
|
||||
logout() {
|
||||
this._userService.logout();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,29 @@
|
||||
import {Component} from '@angular/core';
|
||||
import {UserService} from "../../user/user.service";
|
||||
import {AppStateService} from "../../state/app-state.service";
|
||||
import {LanguageService} from "../../i18n/language.service";
|
||||
import { Component } from '@angular/core';
|
||||
import { UserService } from '../../user/user.service';
|
||||
import { AppStateService } from '../../state/app-state.service';
|
||||
import { LanguageService } from '../../i18n/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-base-screen',
|
||||
templateUrl: './base-screen.component.html',
|
||||
styleUrls: ['./base-screen.component.scss']
|
||||
selector: 'redaction-base-screen',
|
||||
templateUrl: './base-screen.component.html',
|
||||
styleUrls: ['./base-screen.component.scss']
|
||||
})
|
||||
export class BaseScreenComponent {
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _languageService: LanguageService,
|
||||
private readonly _userService: UserService
|
||||
) {}
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
private readonly _languageService: LanguageService,
|
||||
private readonly _userService: UserService) {
|
||||
get user() {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
}
|
||||
logout() {
|
||||
this._userService.logout();
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this._userService.user;
|
||||
}
|
||||
|
||||
logout() {
|
||||
this._userService.logout();
|
||||
}
|
||||
|
||||
changeLanguage(language: string) {
|
||||
this._languageService.changeLanguage(language);
|
||||
}
|
||||
changeLanguage(language: string) {
|
||||
this._languageService.changeLanguage(language);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export enum FileType {
|
||||
ORIGINAL = 'ORIGINAL',
|
||||
ANNOTATED = 'ANNOTATED',
|
||||
REDACTED = 'REDACTED'
|
||||
ORIGINAL = 'ORIGINAL',
|
||||
ANNOTATED = 'ANNOTATED',
|
||||
REDACTED = 'REDACTED'
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<div class="page">
|
||||
<div #viewer [id]="fileStatus.fileId" class="viewer"></div>
|
||||
<div #viewer [id]="fileStatus.fileId" class="viewer"></div>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.viewer {
|
||||
height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@ -1,38 +1,58 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Observable, of} from "rxjs";
|
||||
import {tap} from "rxjs/operators";
|
||||
import {FileUploadControllerService} from '@redaction/red-ui-http';
|
||||
import {FileType} from "../model/file-type";
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { FileUploadControllerService } from '@redaction/red-ui-http';
|
||||
import { FileType } from '../model/file-type';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileDownloadService {
|
||||
constructor(private readonly _fileUploadControllerService: FileUploadControllerService) {
|
||||
}
|
||||
constructor(private readonly _fileUploadControllerService: FileUploadControllerService) {}
|
||||
|
||||
|
||||
loadFile(fileType: FileType | string, fileId: string, saveTo: (fileData) => void = () => null, fetch: () => any = () => null): Observable<any> {
|
||||
let fileObs$: Observable<any>;
|
||||
switch (fileType) {
|
||||
case FileType.ANNOTATED:
|
||||
fileObs$ = fetch() ? of(fetch()) : this._fileUploadControllerService.downloadAnnotatedFile(fileId, true, 'body').pipe(tap(data => {
|
||||
saveTo(data);
|
||||
}));
|
||||
break;
|
||||
case FileType.REDACTED:
|
||||
fileObs$ = fetch() ? of(fetch()) : this._fileUploadControllerService.downloadRedactedFile(fileId, true, 'body').pipe(tap(data => {
|
||||
saveTo(data);
|
||||
}));
|
||||
break;
|
||||
case FileType.ORIGINAL:
|
||||
default:
|
||||
fileObs$ = fetch() ? of(fetch()) : this._fileUploadControllerService.downloadOriginalFile(fileId, true, 'body')
|
||||
.pipe(tap(data => {
|
||||
saveTo(data);
|
||||
}));
|
||||
break;
|
||||
loadFile(
|
||||
fileType: FileType | string,
|
||||
fileId: string,
|
||||
saveTo: (fileData) => void = () => null,
|
||||
fetch: () => any = () => null
|
||||
): Observable<any> {
|
||||
let fileObs$: Observable<any>;
|
||||
switch (fileType) {
|
||||
case FileType.ANNOTATED:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadAnnotatedFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
case FileType.REDACTED:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadRedactedFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
case FileType.ORIGINAL:
|
||||
default:
|
||||
fileObs$ = fetch()
|
||||
? of(fetch())
|
||||
: this._fileUploadControllerService
|
||||
.downloadOriginalFile(fileId, true, 'body')
|
||||
.pipe(
|
||||
tap((data) => {
|
||||
saveTo(data);
|
||||
})
|
||||
);
|
||||
break;
|
||||
}
|
||||
return fileObs$;
|
||||
}
|
||||
return fileObs$;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,25 +2,24 @@ import { Injectable } from '@angular/core';
|
||||
import { AnnotationFilters } from '../../../utils/types';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FiltersService {
|
||||
constructor() {
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
private _filters: AnnotationFilters = {
|
||||
hint: {
|
||||
hint_only: false,
|
||||
vertebrate: false,
|
||||
names: false,
|
||||
},
|
||||
redaction: false,
|
||||
comment: false,
|
||||
suggestion: false,
|
||||
ignore: false,
|
||||
}
|
||||
private _filters: AnnotationFilters = {
|
||||
hint: {
|
||||
hint_only: false,
|
||||
vertebrate: false,
|
||||
names: false
|
||||
},
|
||||
redaction: false,
|
||||
comment: false,
|
||||
suggestion: false,
|
||||
ignore: false
|
||||
};
|
||||
|
||||
public get filters(): AnnotationFilters {
|
||||
return JSON.parse(JSON.stringify(this._filters));
|
||||
}
|
||||
public get filters(): AnnotationFilters {
|
||||
return JSON.parse(JSON.stringify(this._filters));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,21 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from "@angular/router";
|
||||
import {AppStateService} from "./app-state.service";
|
||||
import {UserService} from "../user/user.service";
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
|
||||
import { AppStateService } from './app-state.service';
|
||||
import { UserService } from '../user/user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AppStateGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService
|
||||
) {}
|
||||
|
||||
constructor(private readonly _appStateService: AppStateService, private readonly _userService: UserService) {
|
||||
}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||
|
||||
await this._userService.loadAllUsersIfNecessary()
|
||||
await this._appStateService.loadAllProjectsIfNecessary();
|
||||
|
||||
return true;
|
||||
}
|
||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||
await this._userService.loadAllUsersIfNecessary();
|
||||
await this._appStateService.loadAllProjectsIfNecessary();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
<section>
|
||||
<ngx-dropzone (change)="handleFileInput($event.addedFiles)"
|
||||
class="file-drop-zone">
|
||||
<ngx-dropzone-label>{{"project-overview.upload-files.label"| translate}}</ngx-dropzone-label>
|
||||
</ngx-dropzone>
|
||||
<input type="file" multiple hidden/>
|
||||
<ngx-dropzone (change)="handleFileInput($event.addedFiles)" class="file-drop-zone">
|
||||
<ngx-dropzone-label>{{
|
||||
'project-overview.upload-files.label' | translate
|
||||
}}</ngx-dropzone-label>
|
||||
</ngx-dropzone>
|
||||
<input type="file" multiple hidden />
|
||||
|
||||
<button (click)="close()" class="close-icon" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
<button (click)="close()" class="close-icon" mat-icon-button>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
@ -1,26 +1,25 @@
|
||||
section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1000;
|
||||
padding: 12px;
|
||||
background: white;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
z-index: 1000;
|
||||
padding: 12px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.file-drop-zone {
|
||||
background: white;
|
||||
height: calc(100% - 26px);
|
||||
width: calc(100% - 26px);
|
||||
background: white;
|
||||
height: calc(100% - 26px);
|
||||
width: calc(100% - 26px);
|
||||
}
|
||||
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
z-index: 1100;
|
||||
top: 20px;
|
||||
right: 40px;
|
||||
position: absolute;
|
||||
z-index: 1100;
|
||||
top: 20px;
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
@ -1,46 +1,46 @@
|
||||
import {Component, HostListener, OnInit} from '@angular/core';
|
||||
import {FileUploadService} from "../file-upload.service";
|
||||
import {FileUploadModel} from "../model/file-upload.model";
|
||||
import {OverlayRef} from "@angular/cdk/overlay";
|
||||
import {UploadStatusOverlayService} from "../upload-status-dialog/service/upload-status-overlay.service";
|
||||
import { Component, HostListener, OnInit } from '@angular/core';
|
||||
import { FileUploadService } from '../file-upload.service';
|
||||
import { FileUploadModel } from '../model/file-upload.model';
|
||||
import { OverlayRef } from '@angular/cdk/overlay';
|
||||
import { UploadStatusOverlayService } from '../upload-status-dialog/service/upload-status-overlay.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-drop',
|
||||
templateUrl: './file-drop.component.html',
|
||||
styleUrls: ['./file-drop.component.scss']
|
||||
selector: 'redaction-file-drop',
|
||||
templateUrl: './file-drop.component.html',
|
||||
styleUrls: ['./file-drop.component.scss']
|
||||
})
|
||||
export class FileDropComponent implements OnInit {
|
||||
constructor(
|
||||
private readonly _dialogRef: OverlayRef,
|
||||
private readonly _fileUploadService: FileUploadService,
|
||||
private _uploadStatusOverlayService: UploadStatusOverlayService
|
||||
) {}
|
||||
|
||||
constructor(private readonly _dialogRef: OverlayRef, private readonly _fileUploadService: FileUploadService, private _uploadStatusOverlayService: UploadStatusOverlayService) {
|
||||
}
|
||||
ngOnInit() {}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
close() {
|
||||
this._dialogRef.detach();
|
||||
}
|
||||
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
onKeydownHandler(evt: KeyboardEvent) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
handleFileInput(files: FileList | File[]) {
|
||||
|
||||
const uploadFiles: FileUploadModel[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
uploadFiles.push({
|
||||
file: file,
|
||||
progress: 0,
|
||||
completed: false,
|
||||
error: null
|
||||
})
|
||||
close() {
|
||||
this._dialogRef.detach();
|
||||
}
|
||||
|
||||
this._fileUploadService.uploadFiles(uploadFiles);
|
||||
this._uploadStatusOverlayService.openStatusOverlay();
|
||||
this._dialogRef.detach();
|
||||
}
|
||||
@HostListener('document:keydown.escape', ['$event'])
|
||||
onKeydownHandler(evt: KeyboardEvent) {
|
||||
this.close();
|
||||
}
|
||||
|
||||
handleFileInput(files: FileList | File[]) {
|
||||
const uploadFiles: FileUploadModel[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
uploadFiles.push({
|
||||
file: file,
|
||||
progress: 0,
|
||||
completed: false,
|
||||
error: null
|
||||
});
|
||||
}
|
||||
|
||||
this._fileUploadService.uploadFiles(uploadFiles);
|
||||
this._uploadStatusOverlayService.openStatusOverlay();
|
||||
this._dialogRef.detach();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,69 +1,64 @@
|
||||
import {Injectable, Injector} from "@angular/core";
|
||||
import {Overlay} from "@angular/cdk/overlay";
|
||||
import {FileDropComponent} from "../file-drop.component";
|
||||
import {ComponentPortal} from "@angular/cdk/portal";
|
||||
import {OverlayRef} from "@angular/cdk/overlay";
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Overlay } from '@angular/cdk/overlay';
|
||||
import { FileDropComponent } from '../file-drop.component';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import { OverlayRef } from '@angular/cdk/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileDropOverlayService {
|
||||
private readonly _dropOverlayRef: OverlayRef;
|
||||
|
||||
private readonly _dropOverlayRef: OverlayRef;
|
||||
|
||||
constructor(private overlay: Overlay, private readonly _injector: Injector) {
|
||||
this._dropOverlayRef = this.overlay.create({
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
});
|
||||
}
|
||||
|
||||
dragListener = () => {
|
||||
this.openFileDropOverlay();
|
||||
};
|
||||
mouseOut = e => {
|
||||
if (e.toElement == null && e.relatedTarget == null) {
|
||||
this.closeFileDropOverlay();
|
||||
constructor(private overlay: Overlay, private readonly _injector: Injector) {
|
||||
this._dropOverlayRef = this.overlay.create({
|
||||
height: '100vh',
|
||||
width: '100vw'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
initFileDropHandling() {
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.addEventListener('dragenter', this.dragListener, false);
|
||||
document.getElementsByTagName('body')[0].addEventListener('mouseout', this.mouseOut, false);
|
||||
}
|
||||
dragListener = () => {
|
||||
this.openFileDropOverlay();
|
||||
};
|
||||
mouseOut = (e) => {
|
||||
if (e.toElement == null && e.relatedTarget == null) {
|
||||
this.closeFileDropOverlay();
|
||||
}
|
||||
};
|
||||
|
||||
cleanupFileDropHandling() {
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.removeEventListener('dragenter', this.dragListener, false);
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.removeEventListener('mouseout', this.mouseOut, false);
|
||||
}
|
||||
|
||||
|
||||
private _createInjector() {
|
||||
return Injector.create({
|
||||
providers: [{provide: OverlayRef, useValue: this._dropOverlayRef}],
|
||||
parent: this._injector,
|
||||
})
|
||||
}
|
||||
|
||||
openFileDropOverlay() {
|
||||
const component = new ComponentPortal(FileDropComponent, null, this._createInjector());
|
||||
if (!this._dropOverlayRef.hasAttached()) {
|
||||
this._dropOverlayRef.attach(component);
|
||||
initFileDropHandling() {
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.addEventListener('dragenter', this.dragListener, false);
|
||||
document.getElementsByTagName('body')[0].addEventListener('mouseout', this.mouseOut, false);
|
||||
}
|
||||
}
|
||||
|
||||
closeFileDropOverlay() {
|
||||
if (this._dropOverlayRef) {
|
||||
this._dropOverlayRef.detach();
|
||||
cleanupFileDropHandling() {
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.removeEventListener('dragenter', this.dragListener, false);
|
||||
document
|
||||
.getElementsByTagName('body')[0]
|
||||
.removeEventListener('mouseout', this.mouseOut, false);
|
||||
}
|
||||
}
|
||||
|
||||
private _createInjector() {
|
||||
return Injector.create({
|
||||
providers: [{ provide: OverlayRef, useValue: this._dropOverlayRef }],
|
||||
parent: this._injector
|
||||
});
|
||||
}
|
||||
|
||||
openFileDropOverlay() {
|
||||
const component = new ComponentPortal(FileDropComponent, null, this._createInjector());
|
||||
if (!this._dropOverlayRef.hasAttached()) {
|
||||
this._dropOverlayRef.attach(component);
|
||||
}
|
||||
}
|
||||
|
||||
closeFileDropOverlay() {
|
||||
if (this._dropOverlayRef) {
|
||||
this._dropOverlayRef.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +1,31 @@
|
||||
import {NgModule} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatListModule} from '@angular/material/list';
|
||||
import {MatProgressBarModule} from '@angular/material/progress-bar';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {FileDropComponent} from './file-drop/file-drop.component';
|
||||
import {OverlayModule} from '@angular/cdk/overlay';
|
||||
import {UploadStatusOverlay} from './upload-status-dialog/upload-status-overlay.component';
|
||||
import {NgxDropzoneModule} from "ngx-dropzone";
|
||||
import {TranslateModule} from "@ngx-translate/core";
|
||||
import {MatButtonModule} from "@angular/material/button";
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { FileDropComponent } from './file-drop/file-drop.component';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { UploadStatusOverlay } from './upload-status-dialog/upload-status-overlay.component';
|
||||
import { NgxDropzoneModule } from 'ngx-dropzone';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
MatListModule,
|
||||
NgxDropzoneModule,
|
||||
MatProgressBarModule,
|
||||
OverlayModule,
|
||||
MatButtonModule,
|
||||
],
|
||||
declarations: [FileDropComponent, UploadStatusOverlay],
|
||||
providers: [],
|
||||
entryComponents: [FileDropComponent, UploadStatusOverlay],
|
||||
exports: [FileDropComponent, UploadStatusOverlay]
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
MatTooltipModule,
|
||||
TranslateModule,
|
||||
MatListModule,
|
||||
NgxDropzoneModule,
|
||||
MatProgressBarModule,
|
||||
OverlayModule,
|
||||
MatButtonModule
|
||||
],
|
||||
declarations: [FileDropComponent, UploadStatusOverlay],
|
||||
providers: [],
|
||||
entryComponents: [FileDropComponent, UploadStatusOverlay],
|
||||
exports: [FileDropComponent, UploadStatusOverlay]
|
||||
})
|
||||
export class FileUploadModule {
|
||||
}
|
||||
export class FileUploadModule {}
|
||||
|
||||
@ -1,47 +1,56 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {FileUploadModel} from "./model/file-upload.model";
|
||||
import {FileUploadControllerService} from "@redaction/red-ui-http";
|
||||
import {AppStateService} from "../state/app-state.service";
|
||||
import {HttpEventType} from "@angular/common/http";
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FileUploadModel } from './model/file-upload.model';
|
||||
import { FileUploadControllerService } from '@redaction/red-ui-http';
|
||||
import { AppStateService } from '../state/app-state.service';
|
||||
import { HttpEventType } from '@angular/common/http';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileUploadService {
|
||||
files: FileUploadModel[] = [];
|
||||
|
||||
files: FileUploadModel[] = [];
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService
|
||||
) {}
|
||||
|
||||
constructor(
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||
) {
|
||||
}
|
||||
uploadFiles(files: FileUploadModel[]) {
|
||||
this.files.push(...files);
|
||||
files.forEach((newFile) => {
|
||||
this._fileUploadControllerService
|
||||
.uploadFileForm(
|
||||
newFile.file,
|
||||
this._appStateService.activeProject.project.projectId,
|
||||
'events',
|
||||
true
|
||||
)
|
||||
.subscribe(
|
||||
(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
newFile.progress = Math.round(
|
||||
(event.loaded / (event.total || event.loaded)) * 100
|
||||
);
|
||||
}
|
||||
if (event.type === HttpEventType.Response) {
|
||||
if (event.status < 300) {
|
||||
newFile.progress = 100;
|
||||
newFile.completed = true;
|
||||
} else {
|
||||
newFile.completed = true;
|
||||
newFile.error = event.body;
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
newFile.completed = true;
|
||||
newFile.error = error;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
uploadFiles(files: FileUploadModel[]) {
|
||||
this.files.push(...files);
|
||||
files.forEach(newFile => {
|
||||
this._fileUploadControllerService.uploadFileForm(newFile.file, this._appStateService.activeProject.project.projectId, 'events', true).subscribe((event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
newFile.progress = Math.round((event.loaded / (event.total || event.loaded) * 100));
|
||||
}
|
||||
if (event.type === HttpEventType.Response) {
|
||||
if (event.status < 300) {
|
||||
newFile.progress = 100;
|
||||
newFile.completed = true;
|
||||
} else {
|
||||
newFile.completed = true;
|
||||
newFile.error = event.body;
|
||||
}
|
||||
}
|
||||
}, (error) => {
|
||||
newFile.completed = true;
|
||||
newFile.error = error;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stopAllUploads() {
|
||||
this.files = [];
|
||||
}
|
||||
stopAllUploads() {
|
||||
this.files = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
export interface FileUploadModel {
|
||||
|
||||
file: File;
|
||||
progress: number;
|
||||
completed: boolean;
|
||||
error: any;
|
||||
|
||||
file: File;
|
||||
progress: number;
|
||||
completed: boolean;
|
||||
error: any;
|
||||
}
|
||||
|
||||
@ -1,41 +1,38 @@
|
||||
import {Injectable, Injector} from "@angular/core";
|
||||
import {Overlay, OverlayRef} from "@angular/cdk/overlay";
|
||||
import {ComponentPortal} from "@angular/cdk/portal";
|
||||
import {UploadStatusOverlay} from "../upload-status-overlay.component";
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import { UploadStatusOverlay } from '../upload-status-overlay.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UploadStatusOverlayService {
|
||||
private readonly _statusOverlayRef: OverlayRef;
|
||||
|
||||
private readonly _statusOverlayRef: OverlayRef;
|
||||
|
||||
constructor(private overlay: Overlay, private readonly _injector: Injector) {
|
||||
this._statusOverlayRef = this.overlay.create({
|
||||
height: '500px',
|
||||
width: '300px',
|
||||
});
|
||||
}
|
||||
|
||||
private _createInjector() {
|
||||
return Injector.create({
|
||||
providers: [{provide: OverlayRef, useValue: this._statusOverlayRef}],
|
||||
parent: this._injector,
|
||||
})
|
||||
}
|
||||
|
||||
openStatusOverlay() {
|
||||
const component = new ComponentPortal(UploadStatusOverlay, null, this._createInjector());
|
||||
if (!this._statusOverlayRef.hasAttached()) {
|
||||
this._statusOverlayRef.attach(component);
|
||||
constructor(private overlay: Overlay, private readonly _injector: Injector) {
|
||||
this._statusOverlayRef = this.overlay.create({
|
||||
height: '500px',
|
||||
width: '300px'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
closeStatusOverlay() {
|
||||
if (this._statusOverlayRef) {
|
||||
this._statusOverlayRef.detach();
|
||||
private _createInjector() {
|
||||
return Injector.create({
|
||||
providers: [{ provide: OverlayRef, useValue: this._statusOverlayRef }],
|
||||
parent: this._injector
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openStatusOverlay() {
|
||||
const component = new ComponentPortal(UploadStatusOverlay, null, this._createInjector());
|
||||
if (!this._statusOverlayRef.hasAttached()) {
|
||||
this._statusOverlayRef.attach(component);
|
||||
}
|
||||
}
|
||||
|
||||
closeStatusOverlay() {
|
||||
if (this._statusOverlayRef) {
|
||||
this._statusOverlayRef.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,77 +1,68 @@
|
||||
<section class="red-upload-overlay mat-elevation-z4">
|
||||
<div class="red-upload-header" (click)="collapsed = !collapsed">
|
||||
<div class="title">
|
||||
{{ 'upload-status.dialog.title.label' | translate: {p1: uploadService.files.length} }}
|
||||
</div>
|
||||
<div class="collapse-icon" *ngIf="!collapsed">
|
||||
<mat-icon svgIcon="red:arrow-down"></mat-icon>
|
||||
</div>
|
||||
<div class="collapse-icon" *ngIf="collapsed">
|
||||
<mat-icon svgIcon="red:arrow-up"></mat-icon>
|
||||
</div>
|
||||
<div (click)="closeDialog()" class="close-icon">
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="collapsed">
|
||||
<div class="upload-list">
|
||||
<div
|
||||
*ngFor="let model of uploadService.files"
|
||||
class="upload-list-item"
|
||||
>
|
||||
<div class="upload-line">
|
||||
<div
|
||||
class="upload-file-name"
|
||||
[matTooltip]="model.file?.name"
|
||||
>
|
||||
{{ model.file?.name }}
|
||||
</div>
|
||||
<div class="upload-progress" *ngIf="!model.completed && model.progress < 100">
|
||||
{{ model.progress }}%
|
||||
</div>
|
||||
<div class="upload-progress error" *ngIf="model.completed && model.error">
|
||||
<mat-icon svgIcon="red:error"></mat-icon>
|
||||
</div>
|
||||
<div class="upload-progress ok" *ngIf="model.completed && !model.error">
|
||||
<mat-icon svgIcon="red:check"></mat-icon>
|
||||
</div>
|
||||
<div class="red-upload-header" (click)="collapsed = !collapsed">
|
||||
<div class="title">
|
||||
{{ 'upload-status.dialog.title.label' | translate: { p1: uploadService.files.length } }}
|
||||
</div>
|
||||
<div class="upload-line" *ngIf="model.completed && model.error">
|
||||
<div
|
||||
class="upload-file-name error"
|
||||
[matTooltip]="model.error.message"
|
||||
>
|
||||
{{ model.error.message }}
|
||||
</div>
|
||||
<div class="collapse-icon" *ngIf="!collapsed">
|
||||
<mat-icon svgIcon="red:arrow-down"></mat-icon>
|
||||
</div>
|
||||
<div class="collapse-icon" *ngIf="collapsed">
|
||||
<mat-icon svgIcon="red:arrow-up"></mat-icon>
|
||||
</div>
|
||||
<div (click)="closeDialog()" class="close-icon">
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div [hidden]="collapsed">
|
||||
<div class="upload-list">
|
||||
<div *ngFor="let model of uploadService.files" class="upload-list-item">
|
||||
<div class="upload-line">
|
||||
<div class="upload-file-name" [matTooltip]="model.file?.name">
|
||||
{{ model.file?.name }}
|
||||
</div>
|
||||
<div class="upload-progress" *ngIf="!model.completed && model.progress < 100">
|
||||
{{ model.progress }}%
|
||||
</div>
|
||||
<div class="upload-progress error" *ngIf="model.completed && model.error">
|
||||
<mat-icon svgIcon="red:error"></mat-icon>
|
||||
</div>
|
||||
<div class="upload-progress ok" *ngIf="model.completed && !model.error">
|
||||
<mat-icon svgIcon="red:check"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-line" *ngIf="model.completed && model.error">
|
||||
<div class="upload-file-name error" [matTooltip]="model.error.message">
|
||||
{{ model.error.message }}
|
||||
</div>
|
||||
|
||||
<div class="upload-progress">
|
||||
|
||||
<div
|
||||
class="error-action"
|
||||
(click)="uploadItem(model)"
|
||||
[matTooltip]="'upload-status.dialog.actions.re-upload.label' | translate"
|
||||
>
|
||||
<mat-icon svgIcon="red:refresh"></mat-icon>key
|
||||
<div class="upload-progress">
|
||||
<div
|
||||
class="error-action"
|
||||
(click)="uploadItem(model)"
|
||||
[matTooltip]="
|
||||
'upload-status.dialog.actions.re-upload.label' | translate
|
||||
"
|
||||
>
|
||||
<mat-icon svgIcon="red:refresh"></mat-icon>key
|
||||
</div>
|
||||
<div
|
||||
class="error-action"
|
||||
(click)="cancelItem(model)"
|
||||
[matTooltip]="'upload-status.dialog.actions.cancel.label' | translate"
|
||||
>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-line *ngIf="!model.completed" class="upload-progress">
|
||||
<mat-progress-bar
|
||||
mode="determinate"
|
||||
color="primary"
|
||||
[value]="model.progress"
|
||||
*ngIf="model.progress !== 100"
|
||||
></mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="error-action"
|
||||
(click)="cancelItem(model)"
|
||||
[matTooltip]="'upload-status.dialog.actions.cancel.label' | translate"
|
||||
>
|
||||
<mat-icon svgIcon="red:close"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mat-line *ngIf="!model.completed" class="upload-progress">
|
||||
<mat-progress-bar
|
||||
mode="determinate"
|
||||
color="primary"
|
||||
[value]="model.progress"
|
||||
*ngIf="model.progress !== 100"
|
||||
></mat-progress-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@ -1,91 +1,89 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
section {
|
||||
background: white;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
border: 2px solid $grey-1;
|
||||
background: white;
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 10px;
|
||||
border: 2px solid $grey-1;
|
||||
}
|
||||
|
||||
|
||||
.upload-list {
|
||||
max-height: 400px;
|
||||
max-width: 400px;
|
||||
overflow: auto;
|
||||
max-height: 400px;
|
||||
max-width: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.red-upload-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
background: $grey-1;
|
||||
color: $white;
|
||||
width: 380px;
|
||||
cursor: pointer;
|
||||
|
||||
mat-icon {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
background: $grey-1;
|
||||
color: $white;
|
||||
}
|
||||
width: 380px;
|
||||
cursor: pointer;
|
||||
|
||||
mat-icon {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-icon {
|
||||
padding-left: 8px;
|
||||
padding-left: 8px;
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
}
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
color: $white;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
.upload-list-item {
|
||||
padding: 8px;
|
||||
padding: 8px;
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.upload-line {
|
||||
display: flex !important;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
justify-content: flex-start;
|
||||
|
||||
.upload-file-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
padding-right: 50px;
|
||||
|
||||
&.error {
|
||||
color: $red-1;
|
||||
padding-right: 60px;
|
||||
}
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
.upload-line {
|
||||
display: flex !important;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
justify-content: flex-start;
|
||||
|
||||
&.error {
|
||||
color: $red-1;
|
||||
}
|
||||
.upload-file-name {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
padding-right: 50px;
|
||||
|
||||
&.ok {
|
||||
color: $blue-1;
|
||||
}
|
||||
&.error {
|
||||
color: $red-1;
|
||||
padding-right: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
&.error {
|
||||
color: $red-1;
|
||||
}
|
||||
|
||||
&.ok {
|
||||
color: $blue-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,39 +1,32 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {FileUploadModel} from "../model/file-upload.model";
|
||||
import {FileUploadService} from "../file-upload.service";
|
||||
import {OverlayRef} from "@angular/cdk/overlay";
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FileUploadModel } from '../model/file-upload.model';
|
||||
import { FileUploadService } from '../file-upload.service';
|
||||
import { OverlayRef } from '@angular/cdk/overlay';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-upload-status-overlay',
|
||||
templateUrl: './upload-status-overlay.component.html',
|
||||
styleUrls: ['./upload-status-overlay.component.scss']
|
||||
selector: 'redaction-upload-status-overlay',
|
||||
templateUrl: './upload-status-overlay.component.html',
|
||||
styleUrls: ['./upload-status-overlay.component.scss']
|
||||
})
|
||||
export class UploadStatusOverlay implements OnInit {
|
||||
collapsed = false;
|
||||
|
||||
collapsed = false;
|
||||
constructor(
|
||||
public readonly uploadService: FileUploadService,
|
||||
private readonly _overlayRef: OverlayRef
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
constructor(public readonly uploadService: FileUploadService, private readonly _overlayRef: OverlayRef) {
|
||||
}
|
||||
cancelItem(item: FileUploadModel) {}
|
||||
|
||||
uploadItem(item: FileUploadModel) {
|
||||
item.completed = false;
|
||||
item.error = null;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
}
|
||||
|
||||
cancelItem(item: FileUploadModel) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
uploadItem(item: FileUploadModel) {
|
||||
item.completed = false;
|
||||
item.error = null;
|
||||
|
||||
}
|
||||
|
||||
closeDialog() {
|
||||
this.uploadService.stopAllUploads();
|
||||
this._overlayRef.detach();
|
||||
}
|
||||
closeDialog() {
|
||||
this.uploadService.stopAllUploads();
|
||||
this._overlayRef.detach();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,111 +1,117 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {KeycloakService} from 'keycloak-angular';
|
||||
import {KeycloakProfile} from 'keycloak-js';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { KeycloakService } from 'keycloak-angular';
|
||||
import { KeycloakProfile } from 'keycloak-js';
|
||||
import jwt_decode from 'jwt-decode';
|
||||
import {User, UserControllerService} from '@redaction/red-ui-http';
|
||||
import { User, UserControllerService } from '@redaction/red-ui-http';
|
||||
|
||||
export class UserWrapper {
|
||||
constructor(private _currentUser: KeycloakProfile, public roles: string[], public id: string) {}
|
||||
|
||||
constructor(private _currentUser: KeycloakProfile, public roles: string[], public id: string) {
|
||||
}
|
||||
get name() {
|
||||
return this._currentUser.firstName + ' ' + this._currentUser.lastName;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._currentUser.firstName + ' ' + this._currentUser.lastName;
|
||||
}
|
||||
get isManager() {
|
||||
return this.roles.indexOf('RED_MANAGER') >= 0;
|
||||
}
|
||||
|
||||
get isManager() {
|
||||
return this.roles.indexOf('RED_MANAGER') >= 0;
|
||||
}
|
||||
get isUser() {
|
||||
return this.roles.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
|
||||
get isAdmin() {
|
||||
return this.roles.indexOf('RED_ADMIN') >= 0;
|
||||
}
|
||||
|
||||
get isUser() {
|
||||
return this.roles.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
|
||||
|
||||
get isAdmin() {
|
||||
return this.roles.indexOf('RED_ADMIN') >= 0;
|
||||
}
|
||||
|
||||
get hasAnyREDRoles() {
|
||||
return this.isUser || this.isManager || this.isAdmin;
|
||||
}
|
||||
|
||||
get hasAnyREDRoles() {
|
||||
return this.isUser || this.isManager || this.isAdmin;
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserService {
|
||||
private _currentUser: UserWrapper;
|
||||
private _allUsers: User[];
|
||||
private _currentUser: UserWrapper;
|
||||
private _allUsers: User[];
|
||||
|
||||
constructor(private readonly _keycloakService: KeycloakService,
|
||||
private readonly _userControllerService: UserControllerService) {
|
||||
}
|
||||
constructor(
|
||||
private readonly _keycloakService: KeycloakService,
|
||||
private readonly _userControllerService: UserControllerService
|
||||
) {}
|
||||
|
||||
logout() {
|
||||
this._keycloakService.logout(window.location.origin);
|
||||
}
|
||||
|
||||
get userId() {
|
||||
return this._currentUser.id;
|
||||
}
|
||||
|
||||
get allUsers() {
|
||||
return this._allUsers;
|
||||
}
|
||||
|
||||
get managerUsers() {
|
||||
return this._allUsers.filter(u => u.roles.indexOf('RED_MANAGER') >= 0);
|
||||
}
|
||||
|
||||
async loadAllUsersIfNecessary() {
|
||||
if (!this._allUsers) {
|
||||
await this.loadAllUsers();
|
||||
logout() {
|
||||
this._keycloakService.logout(window.location.origin);
|
||||
}
|
||||
}
|
||||
|
||||
async loadAllUsers() {
|
||||
const allUsers = await this._userControllerService.getUsers({}, 0, 100).toPromise();
|
||||
this._allUsers = allUsers.users.filter(u => this._hasAnyRedRole(u));
|
||||
return allUsers;
|
||||
}
|
||||
get userId() {
|
||||
return this._currentUser.id;
|
||||
}
|
||||
|
||||
async loadCurrentUser() {
|
||||
const token = await this._keycloakService.getToken();
|
||||
const decoded = jwt_decode(token);
|
||||
const userId = decoded.sub;
|
||||
this._currentUser = new UserWrapper(await this._keycloakService.loadUserProfile(false), this._keycloakService.getUserRoles(true), userId);
|
||||
}
|
||||
get allUsers() {
|
||||
return this._allUsers;
|
||||
}
|
||||
|
||||
get user() {
|
||||
return this._currentUser;
|
||||
}
|
||||
get managerUsers() {
|
||||
return this._allUsers.filter((u) => u.roles.indexOf('RED_MANAGER') >= 0);
|
||||
}
|
||||
|
||||
getUserById(id: string) {
|
||||
return this._allUsers.find(u => u.userId === id);
|
||||
}
|
||||
async loadAllUsersIfNecessary() {
|
||||
if (!this._allUsers) {
|
||||
await this.loadAllUsers();
|
||||
}
|
||||
}
|
||||
|
||||
getNameForId(userId: string) {
|
||||
return this.getName(this.getUserById(userId));
|
||||
}
|
||||
async loadAllUsers() {
|
||||
const allUsers = await this._userControllerService.getUsers({}, 0, 100).toPromise();
|
||||
this._allUsers = allUsers.users.filter((u) => this._hasAnyRedRole(u));
|
||||
return allUsers;
|
||||
}
|
||||
|
||||
getName(user?: User) {
|
||||
return user ?
|
||||
(user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : user.username) :
|
||||
undefined;
|
||||
}
|
||||
async loadCurrentUser() {
|
||||
const token = await this._keycloakService.getToken();
|
||||
const decoded = jwt_decode(token);
|
||||
const userId = decoded.sub;
|
||||
this._currentUser = new UserWrapper(
|
||||
await this._keycloakService.loadUserProfile(false),
|
||||
this._keycloakService.getUserRoles(true),
|
||||
userId
|
||||
);
|
||||
}
|
||||
|
||||
isManager(user: User) {
|
||||
return user.roles.indexOf('RED_MANAGER') >= 0;
|
||||
}
|
||||
get user() {
|
||||
return this._currentUser;
|
||||
}
|
||||
|
||||
isUser(user: User) {
|
||||
return user.roles.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
getUserById(id: string) {
|
||||
return this._allUsers.find((u) => u.userId === id);
|
||||
}
|
||||
|
||||
private _hasAnyRedRole(u: User) {
|
||||
return u.roles.indexOf('RED_USER') >= 0 || u.roles.indexOf('RED_MANAGER') >= 0 || u.roles.indexOf('RED_ADMIN') >= 0;
|
||||
}
|
||||
getNameForId(userId: string) {
|
||||
return this.getName(this.getUserById(userId));
|
||||
}
|
||||
|
||||
getName(user?: User) {
|
||||
return user
|
||||
? user.firstName && user.lastName
|
||||
? `${user.firstName} ${user.lastName}`
|
||||
: user.username
|
||||
: undefined;
|
||||
}
|
||||
|
||||
isManager(user: User) {
|
||||
return user.roles.indexOf('RED_MANAGER') >= 0;
|
||||
}
|
||||
|
||||
isUser(user: User) {
|
||||
return user.roles.indexOf('RED_USER') >= 0;
|
||||
}
|
||||
|
||||
private _hasAnyRedRole(u: User) {
|
||||
return (
|
||||
u.roles.indexOf('RED_USER') >= 0 ||
|
||||
u.roles.indexOf('RED_MANAGER') >= 0 ||
|
||||
u.roles.indexOf('RED_ADMIN') >= 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,107 +1,114 @@
|
||||
import {Annotations} from '@pdftron/webviewer';
|
||||
import {AnnotationFilters} from './types';
|
||||
import { Annotations } from '@pdftron/webviewer';
|
||||
import { AnnotationFilters } from './types';
|
||||
|
||||
export class AnnotationUtils {
|
||||
public static sortAnnotations(annotations: Annotations.Annotation[]): Annotations.Annotation[] {
|
||||
return annotations.sort((ann1, ann2) => {
|
||||
if (ann1.getPageNumber() === ann2.getPageNumber()) {
|
||||
if (ann1.getY() === ann2.getY()) {
|
||||
if (ann1.getX() === ann2.getY()) {
|
||||
return 0;
|
||||
}
|
||||
return ann1.getX() < ann2.getX() ? -1 : 1;
|
||||
}
|
||||
return ann1.getY() < ann2.getY() ? -1 : 1;
|
||||
}
|
||||
return ann1.getPageNumber() < ann2.getPageNumber() ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
public static hasSubsections(filter: AnnotationFilters | boolean) {
|
||||
return filter instanceof Object;
|
||||
}
|
||||
|
||||
public static checkedSubkeys(filter: AnnotationFilters | boolean) {
|
||||
return Object.keys(filter).filter(subkey => this.isChecked(filter[subkey])).length;
|
||||
}
|
||||
|
||||
// Only some of the sub-items are selected
|
||||
public static isIndeterminate(filter: AnnotationFilters | boolean): boolean {
|
||||
return this.hasSubsections(filter) ? AnnotationUtils.checkedSubkeys(filter) > 0 && !this.isChecked(filter) : false;
|
||||
}
|
||||
|
||||
// All sub-items are selected
|
||||
public static isChecked(filter: AnnotationFilters | boolean): boolean {
|
||||
return this.hasSubsections(filter) ?
|
||||
AnnotationUtils.checkedSubkeys(filter) === Object.keys(filter).length :
|
||||
filter as boolean;
|
||||
}
|
||||
|
||||
public static hasActiveFilters(filter: AnnotationFilters): boolean {
|
||||
const activeFilters = Object.keys(filter).filter(key => {
|
||||
return this.isChecked(filter[key]) || this.isIndeterminate(filter[key]);
|
||||
});
|
||||
return activeFilters.length > 0;
|
||||
}
|
||||
|
||||
public static parseAnnotations(annotations: Annotations.Annotation[], filters: AnnotationFilters):
|
||||
{ [key: number]: { annotations: Annotations.Annotation[] } } {
|
||||
const obj = {};
|
||||
|
||||
for (const ann of annotations) {
|
||||
const pageNumber = ann.getPageNumber();
|
||||
const type = this.getType(ann);
|
||||
const dictionary = this.getDictionary(ann);
|
||||
|
||||
if (this.hasActiveFilters(filters)) {
|
||||
if (!this.hasSubsections(filters[type]) && !filters[type]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.hasSubsections(filters[type]) && !filters[type][dictionary]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj[pageNumber]) {
|
||||
obj[pageNumber] = {
|
||||
annotations: [],
|
||||
hint: 0,
|
||||
redaction: 0,
|
||||
comment: 0,
|
||||
suggestion: 0,
|
||||
ignore: 0
|
||||
};
|
||||
}
|
||||
obj[pageNumber].annotations.push(ann);
|
||||
obj[pageNumber][type]++;
|
||||
public static sortAnnotations(annotations: Annotations.Annotation[]): Annotations.Annotation[] {
|
||||
return annotations.sort((ann1, ann2) => {
|
||||
if (ann1.getPageNumber() === ann2.getPageNumber()) {
|
||||
if (ann1.getY() === ann2.getY()) {
|
||||
if (ann1.getX() === ann2.getY()) {
|
||||
return 0;
|
||||
}
|
||||
return ann1.getX() < ann2.getX() ? -1 : 1;
|
||||
}
|
||||
return ann1.getY() < ann2.getY() ? -1 : 1;
|
||||
}
|
||||
return ann1.getPageNumber() < ann2.getPageNumber() ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(obj).map(page => {
|
||||
obj[page].annotations = this.sortAnnotations(obj[page].annotations);
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static addAnnotations(initialAnnotations: Annotations.Annotation[], addedAnnotations: Annotations.Annotation[]) {
|
||||
for (const annotation of addedAnnotations) {
|
||||
if (annotation.Id.indexOf(':') > 0) {
|
||||
const found = initialAnnotations.find(a => a.Id === annotation.Id);
|
||||
if(!found) {
|
||||
initialAnnotations.push(annotation);
|
||||
}
|
||||
}
|
||||
public static hasSubsections(filter: AnnotationFilters | boolean) {
|
||||
return filter instanceof Object;
|
||||
}
|
||||
}
|
||||
|
||||
public static getType(annotation: Annotations.Annotation): string {
|
||||
const parts = annotation.Id.split(':');
|
||||
return parts.length >= 1 ? parts[0] : 'n/a';
|
||||
}
|
||||
public static checkedSubkeys(filter: AnnotationFilters | boolean) {
|
||||
return Object.keys(filter).filter((subkey) => this.isChecked(filter[subkey])).length;
|
||||
}
|
||||
|
||||
public static getDictionary(annotation: Annotations.Annotation): string {
|
||||
const parts = annotation.Id.split(':');
|
||||
return parts.length >= 2 ? parts[1] : 'n/a';
|
||||
}
|
||||
// Only some of the sub-items are selected
|
||||
public static isIndeterminate(filter: AnnotationFilters | boolean): boolean {
|
||||
return this.hasSubsections(filter)
|
||||
? AnnotationUtils.checkedSubkeys(filter) > 0 && !this.isChecked(filter)
|
||||
: false;
|
||||
}
|
||||
|
||||
// All sub-items are selected
|
||||
public static isChecked(filter: AnnotationFilters | boolean): boolean {
|
||||
return this.hasSubsections(filter)
|
||||
? AnnotationUtils.checkedSubkeys(filter) === Object.keys(filter).length
|
||||
: (filter as boolean);
|
||||
}
|
||||
|
||||
public static hasActiveFilters(filter: AnnotationFilters): boolean {
|
||||
const activeFilters = Object.keys(filter).filter((key) => {
|
||||
return this.isChecked(filter[key]) || this.isIndeterminate(filter[key]);
|
||||
});
|
||||
return activeFilters.length > 0;
|
||||
}
|
||||
|
||||
public static parseAnnotations(
|
||||
annotations: Annotations.Annotation[],
|
||||
filters: AnnotationFilters
|
||||
): { [key: number]: { annotations: Annotations.Annotation[] } } {
|
||||
const obj = {};
|
||||
|
||||
for (const ann of annotations) {
|
||||
const pageNumber = ann.getPageNumber();
|
||||
const type = this.getType(ann);
|
||||
const dictionary = this.getDictionary(ann);
|
||||
|
||||
if (this.hasActiveFilters(filters)) {
|
||||
if (!this.hasSubsections(filters[type]) && !filters[type]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.hasSubsections(filters[type]) && !filters[type][dictionary]) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj[pageNumber]) {
|
||||
obj[pageNumber] = {
|
||||
annotations: [],
|
||||
hint: 0,
|
||||
redaction: 0,
|
||||
comment: 0,
|
||||
suggestion: 0,
|
||||
ignore: 0
|
||||
};
|
||||
}
|
||||
obj[pageNumber].annotations.push(ann);
|
||||
obj[pageNumber][type]++;
|
||||
}
|
||||
|
||||
Object.keys(obj).map((page) => {
|
||||
obj[page].annotations = this.sortAnnotations(obj[page].annotations);
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static addAnnotations(
|
||||
initialAnnotations: Annotations.Annotation[],
|
||||
addedAnnotations: Annotations.Annotation[]
|
||||
) {
|
||||
for (const annotation of addedAnnotations) {
|
||||
if (annotation.Id.indexOf(':') > 0) {
|
||||
const found = initialAnnotations.find((a) => a.Id === annotation.Id);
|
||||
if (!found) {
|
||||
initialAnnotations.push(annotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static getType(annotation: Annotations.Annotation): string {
|
||||
const parts = annotation.Id.split(':');
|
||||
return parts.length >= 1 ? parts[0] : 'n/a';
|
||||
}
|
||||
|
||||
public static getDictionary(annotation: Annotations.Annotation): string {
|
||||
const parts = annotation.Id.split(':');
|
||||
return parts.length >= 2 ? parts[1] : 'n/a';
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { EventEmitter, Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AppLoadStateService {
|
||||
private _loadingEvent = new EventEmitter();
|
||||
|
||||
@ -1,57 +1,69 @@
|
||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
|
||||
import {Injectable, Injector} from '@angular/core';
|
||||
import {from, Observable, of} from 'rxjs';
|
||||
import {catchError, map, mergeMap, tap} from 'rxjs/operators';
|
||||
import {AppLoadStateService} from "./app-load-state.service";
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree
|
||||
} from '@angular/router';
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { from, Observable, of } from 'rxjs';
|
||||
import { catchError, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { AppLoadStateService } from './app-load-state.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CompositeRouteGuard implements CanActivate {
|
||||
constructor(
|
||||
protected readonly _router: Router,
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _appLoadStateService: AppLoadStateService
|
||||
) {}
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
this._appLoadStateService.pushLoadingEvent(true);
|
||||
let compositeCanActivateObservable: Observable<boolean> = of(true);
|
||||
|
||||
constructor(protected readonly _router: Router, protected readonly _injector: Injector, private readonly _appLoadStateService: AppLoadStateService) {
|
||||
}
|
||||
const routeGuards = route.data.routeGuards;
|
||||
|
||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
|
||||
this._appLoadStateService.pushLoadingEvent(true);
|
||||
let compositeCanActivateObservable: Observable<boolean> = of(true);
|
||||
|
||||
const routeGuards = route.data.routeGuards;
|
||||
|
||||
if (routeGuards) {
|
||||
for (let i = 0; i < routeGuards.length; i++) {
|
||||
const routeGuard = this._injector.get<CanActivate>(routeGuards[i]);
|
||||
let canActivateResult = routeGuard.canActivate(route, state);
|
||||
if (canActivateResult instanceof Promise) {
|
||||
canActivateResult = from(canActivateResult);
|
||||
}
|
||||
if (typeof canActivateResult === 'boolean' || canActivateResult instanceof UrlTree) {
|
||||
canActivateResult = of(canActivateResult);
|
||||
}
|
||||
const canActivatePipe: Observable<boolean> = canActivateResult.pipe(map(m => !!m));
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
mergeMap(bool => {
|
||||
if (!bool) {
|
||||
return of(false);
|
||||
} else {
|
||||
return canActivatePipe;
|
||||
if (routeGuards) {
|
||||
for (let i = 0; i < routeGuards.length; i++) {
|
||||
const routeGuard = this._injector.get<CanActivate>(routeGuards[i]);
|
||||
let canActivateResult = routeGuard.canActivate(route, state);
|
||||
if (canActivateResult instanceof Promise) {
|
||||
canActivateResult = from(canActivateResult);
|
||||
}
|
||||
if (
|
||||
typeof canActivateResult === 'boolean' ||
|
||||
canActivateResult instanceof UrlTree
|
||||
) {
|
||||
canActivateResult = of(canActivateResult);
|
||||
}
|
||||
const canActivatePipe: Observable<boolean> = canActivateResult.pipe(
|
||||
map((m) => !!m)
|
||||
);
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
mergeMap((bool) => {
|
||||
if (!bool) {
|
||||
return of(false);
|
||||
} else {
|
||||
return canActivatePipe;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
tap(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
}),
|
||||
catchError(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
return of(false);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return compositeCanActivateObservable;
|
||||
}
|
||||
|
||||
compositeCanActivateObservable = compositeCanActivateObservable.pipe(
|
||||
tap(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
}),
|
||||
catchError(() => {
|
||||
this._appLoadStateService.pushLoadingEvent(false);
|
||||
return of(false);
|
||||
})
|
||||
);
|
||||
|
||||
return compositeCanActivateObservable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
export function debounce(delay: number = 300): MethodDecorator {
|
||||
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
||||
let timeout = null;
|
||||
|
||||
const original = descriptor.value;
|
||||
|
||||
descriptor.value = function(...args) {
|
||||
descriptor.value = function (...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => original.apply(this, args), delay);
|
||||
};
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import {HttpResponse} from '@angular/common/http';
|
||||
import {saveAs} from 'file-saver';
|
||||
import { HttpResponse } from '@angular/common/http';
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
export function download(event: HttpResponse<Blob>, altName?: string) {
|
||||
const contentDisposition = event.headers.get('Content-Disposition');
|
||||
let fileName;
|
||||
try {
|
||||
fileName = contentDisposition ? contentDisposition.split('filename=')[1].replace(/"/g, '') : undefined;
|
||||
} catch (e) {
|
||||
console.log('failed to parse content-disposition: ', contentDisposition);
|
||||
}
|
||||
saveAs(event.body, fileName ? fileName : altName);
|
||||
const contentDisposition = event.headers.get('Content-Disposition');
|
||||
let fileName;
|
||||
try {
|
||||
fileName = contentDisposition
|
||||
? contentDisposition.split('filename=')[1].replace(/"/g, '')
|
||||
: undefined;
|
||||
} catch (e) {
|
||||
console.log('failed to parse content-disposition: ', contentDisposition);
|
||||
}
|
||||
saveAs(event.body, fileName ? fileName : altName);
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<section class="full-page-load-section" *ngIf="displayed">
|
||||
</section>
|
||||
<section class="full-page-load-section" *ngIf="displayed"></section>
|
||||
<section class="full-page-load-spinner" *ngIf="displayed">
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
<mat-spinner diameter="40"></mat-spinner>
|
||||
</section>
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
@import "../../../assets/styles/red-variables";
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
|
||||
.full-page-load-section, .full-page-load-spinner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
.full-page-load-section,
|
||||
.full-page-load-spinner {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.full-page-load-section {
|
||||
opacity: 0.7;
|
||||
background: $white;
|
||||
z-index: 900;
|
||||
|
||||
opacity: 0.7;
|
||||
background: $white;
|
||||
z-index: 900;
|
||||
}
|
||||
|
||||
.full-page-load-spinner {
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
z-index: 1000;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-full-page-loading-indicator',
|
||||
templateUrl: './full-page-loading-indicator.component.html',
|
||||
styleUrls: ['./full-page-loading-indicator.component.scss']
|
||||
selector: 'redaction-full-page-loading-indicator',
|
||||
templateUrl: './full-page-loading-indicator.component.html',
|
||||
styleUrls: ['./full-page-loading-indicator.component.scss']
|
||||
})
|
||||
export class FullPageLoadingIndicatorComponent {
|
||||
|
||||
@Input() displayed = false;
|
||||
|
||||
export class FullPageLoadingIndicatorComponent {
|
||||
@Input() displayed = false;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export function groupBy(xs: any[], key: string) {
|
||||
return xs.reduce((rv, x) => {
|
||||
(rv[x[key]] = rv[x[key]] || []).push(x);
|
||||
return rv;
|
||||
}, {});
|
||||
return xs.reduce((rv, x) => {
|
||||
(rv[x[key]] = rv[x[key]] || []).push(x);
|
||||
return rv;
|
||||
}, {});
|
||||
}
|
||||
|
||||
17
apps/red-ui/src/app/utils/types.d.ts
vendored
17
apps/red-ui/src/app/utils/types.d.ts
vendored
@ -1,20 +1,15 @@
|
||||
import {FileStatus} from "@redaction/red-ui-http";
|
||||
import { FileStatus } from '@redaction/red-ui-http';
|
||||
|
||||
export type Color = FileStatus.StatusEnum | ProjectStatus.StatusEnum;
|
||||
|
||||
export type AnnotationType =
|
||||
'hint' |
|
||||
'redaction' |
|
||||
'suggestion' |
|
||||
'comment' |
|
||||
'ignore'
|
||||
export type AnnotationType = 'hint' | 'redaction' | 'suggestion' | 'comment' | 'ignore';
|
||||
|
||||
export class SortingOption {
|
||||
label: string;
|
||||
order: string;
|
||||
column: string;
|
||||
label: string;
|
||||
order: string;
|
||||
column: string;
|
||||
}
|
||||
|
||||
export class AnnotationFilters {
|
||||
[key: AnnotationType]: boolean
|
||||
[key: AnnotationType]: boolean;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"OAUTH_URL": "https://keycloak-dev.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"API_URL": "",
|
||||
"PDFTRON_LICENSE": ""
|
||||
"OAUTH_URL": "https://keycloak-dev.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"API_URL": "",
|
||||
"PDFTRON_LICENSE": ""
|
||||
}
|
||||
|
||||
@ -1,466 +1,466 @@
|
||||
{
|
||||
"auth-error": {
|
||||
"heading": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie Ihren Administrator für den Zugriff!"
|
||||
},
|
||||
"heading-with-name-and-link": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie <a href={{adminUrl}} target=_blank >{{adminName}}</a> für den Zugriff!"
|
||||
},
|
||||
"heading-with-name": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie {{adminName}} für den Zugriff!"
|
||||
},
|
||||
"heading-with-link": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie <a href={{adminUrl}} target=_blank >Ihren Administrator</a> für den Zugriff!"
|
||||
},
|
||||
"logout": {
|
||||
"label": "Ausloggen"
|
||||
}
|
||||
},
|
||||
"manual-redaction": {
|
||||
"remove-annotation": {
|
||||
"success": {
|
||||
"label": "Anmerkung Zum Entfernen empfohlen!"
|
||||
},
|
||||
"failed": {
|
||||
"label": "Fehler beim Anfordern der Entfernung von Anmerkungen!"
|
||||
}
|
||||
},
|
||||
"dialog": {
|
||||
"header": {
|
||||
"label": "Manuelle Redaktion hinzufügen"
|
||||
},
|
||||
"add-redaction": {
|
||||
"success": {
|
||||
"label": "Redaktionsvorschlag hinzugefügt!"
|
||||
"auth-error": {
|
||||
"heading": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie Ihren Administrator für den Zugriff!"
|
||||
},
|
||||
"failed": {
|
||||
"label": "Manuelle Redaktion konnte nicht hinzugefügt werden: {{message}}"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"save": {
|
||||
"label": "Manuelle Redaktion speichern"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"text": {
|
||||
"label": "<strong>Ausgewählter Text:</strong> {{value}}"
|
||||
"heading-with-name-and-link": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie <a href={{adminUrl}} target=_blank >{{adminName}}</a> für den Zugriff!"
|
||||
},
|
||||
"dictionary": {
|
||||
"add": {
|
||||
"label": "Zum Wörterbuch hinzufügen"
|
||||
},
|
||||
"label": "Wörterbuch"
|
||||
"heading-with-name": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie {{adminName}} für den Zugriff!"
|
||||
},
|
||||
"reason": {
|
||||
"label": "Grund"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"app-name": {
|
||||
"label": "Redacto"
|
||||
},
|
||||
"upload-status": {
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Datei-Upload"
|
||||
},
|
||||
"actions": {
|
||||
"re-upload": {
|
||||
"label": "Wiederholen Sie den Upload"
|
||||
"heading-with-link": {
|
||||
"label": "Ihr Benutzer verfügt nicht über die erforderlichen RED- * -Rollen, um auf diese Anwendung zuzugreifen. Bitte kontaktieren Sie <a href={{adminUrl}} target=_blank >Ihren Administrator</a> für den Zugriff!"
|
||||
},
|
||||
"cancel": {
|
||||
"label": "Upload abbrechen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pdf-viewer": {
|
||||
"text-popup": {
|
||||
"actions": {
|
||||
"suggestion-redaction": {
|
||||
"label": "Redaktion vorschlagen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"dialog": {
|
||||
"close": {
|
||||
"label": "Dialog schließen"
|
||||
}
|
||||
},
|
||||
"confirmation-dialog": {
|
||||
"title": {
|
||||
"label": "Aktion bestätigen"
|
||||
},
|
||||
"description": {
|
||||
"label": "Diese Aktion muss bestätigt werden. Möchten Sie fortfahren?"
|
||||
},
|
||||
"confirm": {
|
||||
"label": "Ja"
|
||||
},
|
||||
"deny": {
|
||||
"label": "Nein"
|
||||
}
|
||||
}
|
||||
},
|
||||
"top-bar": {
|
||||
"navigation-items": {
|
||||
"projects": {
|
||||
"label": "Projekte"
|
||||
},
|
||||
"my-account": {
|
||||
"label": "Mein Konto",
|
||||
"children": {
|
||||
"language": {
|
||||
"label": "Sprache",
|
||||
"english": {
|
||||
"label": "Englisch"
|
||||
},
|
||||
"german": {
|
||||
"label": "Deutsche"
|
||||
}
|
||||
},
|
||||
"logout": {
|
||||
"logout": {
|
||||
"label": "Ausloggen"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"filter-by": {
|
||||
"label": "Filtern nach:"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status"
|
||||
},
|
||||
"people": {
|
||||
"label": "Menschen"
|
||||
},
|
||||
"due-date": {
|
||||
"label": "Geburtstermin"
|
||||
},
|
||||
"created-on": {
|
||||
"label": "Erstellt am"
|
||||
},
|
||||
"project": {
|
||||
"label": "Projekt"
|
||||
},
|
||||
"document": {
|
||||
"label": "Dokument"
|
||||
}
|
||||
},
|
||||
"project-listing": {
|
||||
"table-header": {
|
||||
"title": {
|
||||
"label": "{{length}} aktive Projekte"
|
||||
},
|
||||
"bulk-select": {
|
||||
"label": "Massenauswahl"
|
||||
},
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"analyzed-pages": {
|
||||
"label": "Analysierte Seiten"
|
||||
},
|
||||
"total-people": {
|
||||
"label": "Insgesamt Menschen"
|
||||
},
|
||||
"charts": {
|
||||
"projects": {
|
||||
"label": "Projekte"
|
||||
"manual-redaction": {
|
||||
"remove-annotation": {
|
||||
"success": {
|
||||
"label": "Anmerkung Zum Entfernen empfohlen!"
|
||||
},
|
||||
"failed": {
|
||||
"label": "Fehler beim Anfordern der Entfernung von Anmerkungen!"
|
||||
}
|
||||
},
|
||||
"total-documents": {
|
||||
"label": "Dokumente insgesamt"
|
||||
"dialog": {
|
||||
"header": {
|
||||
"label": "Manuelle Redaktion hinzufügen"
|
||||
},
|
||||
"add-redaction": {
|
||||
"success": {
|
||||
"label": "Redaktionsvorschlag hinzugefügt!"
|
||||
},
|
||||
"failed": {
|
||||
"label": "Manuelle Redaktion konnte nicht hinzugefügt werden: {{message}}"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"save": {
|
||||
"label": "Manuelle Redaktion speichern"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"text": {
|
||||
"label": "<strong>Ausgewählter Text:</strong> {{value}}"
|
||||
},
|
||||
"dictionary": {
|
||||
"add": {
|
||||
"label": "Zum Wörterbuch hinzufügen"
|
||||
},
|
||||
"label": "Wörterbuch"
|
||||
},
|
||||
"reason": {
|
||||
"label": "Grund"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"add-edit-dialog": {
|
||||
"header-new": {
|
||||
"label": "Neues Projekt"
|
||||
},
|
||||
"header-edit": {
|
||||
"label": "Projekt bearbeiten"
|
||||
},
|
||||
"form": {
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
"app-name": {
|
||||
"label": "Redacto"
|
||||
},
|
||||
"upload-status": {
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Datei-Upload"
|
||||
},
|
||||
"actions": {
|
||||
"re-upload": {
|
||||
"label": "Wiederholen Sie den Upload"
|
||||
},
|
||||
"cancel": {
|
||||
"label": "Upload abbrechen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"pdf-viewer": {
|
||||
"text-popup": {
|
||||
"actions": {
|
||||
"suggestion-redaction": {
|
||||
"label": "Redaktion vorschlagen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"dialog": {
|
||||
"close": {
|
||||
"label": "Dialog schließen"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"label": "Name"
|
||||
"confirmation-dialog": {
|
||||
"title": {
|
||||
"label": "Aktion bestätigen"
|
||||
},
|
||||
"description": {
|
||||
"label": "Diese Aktion muss bestätigt werden. Möchten Sie fortfahren?"
|
||||
},
|
||||
"confirm": {
|
||||
"label": "Ja"
|
||||
},
|
||||
"deny": {
|
||||
"label": "Nein"
|
||||
}
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"save": {
|
||||
"label": "Projekt speichern"
|
||||
},
|
||||
"top-bar": {
|
||||
"navigation-items": {
|
||||
"projects": {
|
||||
"label": "Projekte"
|
||||
},
|
||||
"my-account": {
|
||||
"label": "Mein Konto",
|
||||
"children": {
|
||||
"language": {
|
||||
"label": "Sprache",
|
||||
"english": {
|
||||
"label": "Englisch"
|
||||
},
|
||||
"german": {
|
||||
"label": "Deutsche"
|
||||
}
|
||||
},
|
||||
"logout": {
|
||||
"label": "Ausloggen"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"label": "Projekte"
|
||||
},
|
||||
"edit": {
|
||||
"action": {
|
||||
"label": "Projekt bearbeiten"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"action": {
|
||||
"label": "Projekt löschen"
|
||||
},
|
||||
"delete-failed": {
|
||||
"label": "Projekt konnte nicht gelöscht werden: {{projectName}}"
|
||||
}
|
||||
},
|
||||
"add-new": {
|
||||
"label": "Neues Projekt"
|
||||
},
|
||||
"no-projects": {
|
||||
"label": "Sie haben derzeit keine Projekte. Sie können Ihre Arbeit beginnen, indem Sie eine neue erstellen!"
|
||||
},
|
||||
"sorting": {
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
},
|
||||
"alphabetically": {
|
||||
"label": "Alphabetisch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-details": {
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Dateidetails"
|
||||
},
|
||||
"actions": {
|
||||
"download-redaction-report": {
|
||||
"label": "Redaktionsbericht herunterladen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-details": {
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Projekt Details"
|
||||
},
|
||||
"info": {
|
||||
"file-count": {
|
||||
"label": "Anzahl der Dateien: {{fileCount}}"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"download-redaction-report": {
|
||||
"label": "Redaktionsbericht herunterladen"
|
||||
"filters": {
|
||||
"filter-by": {
|
||||
"label": "Filtern nach:"
|
||||
},
|
||||
"reanalyse-project": {
|
||||
"label": "Projekt erneut analysieren"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-overview": {
|
||||
"table-header": {
|
||||
"title": {
|
||||
"label": "{{length}} Dokumente"
|
||||
},
|
||||
"bulk-select": {
|
||||
"label": "Massenauswahl"
|
||||
},
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
}
|
||||
},
|
||||
"table-col-names": {
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"added-on": {
|
||||
"label": "Hinzugefügt zu"
|
||||
},
|
||||
"needs-work": {
|
||||
"label": "Braucht Arbeit"
|
||||
},
|
||||
"assigned-to": {
|
||||
"label": "Zugewiesen an"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status"
|
||||
}
|
||||
},
|
||||
"sorting": {
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
},
|
||||
"alphabetically": {
|
||||
"label": "Alphabetisch"
|
||||
},
|
||||
"number-of-pages": {
|
||||
"label": "Seitenzahl"
|
||||
},
|
||||
"number-of-analyses": {
|
||||
"label": "Anzahl der Analysen"
|
||||
}
|
||||
},
|
||||
"upload-error": {
|
||||
"label": "Datei konnte nicht hochgeladen werden: {{name}}"
|
||||
},
|
||||
"delete-file-error": {
|
||||
"label": "Fehler beim Löschen der Datei: {{filename}}"
|
||||
},
|
||||
"reanalyse": {
|
||||
"action": {
|
||||
"label": "Datei erneut analysieren"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"action": {
|
||||
"label": "Datei löschen"
|
||||
}
|
||||
},
|
||||
"file-listing": {
|
||||
"file-entry": {
|
||||
"status": {
|
||||
"label": "Status: {{status}}"
|
||||
"label": "Status"
|
||||
},
|
||||
"number-of-pages": {
|
||||
"label": "Anzahl der Seiten: {{numberOfPages}}"
|
||||
"people": {
|
||||
"label": "Menschen"
|
||||
},
|
||||
"number-of-analyses": {
|
||||
"label": "Anzahl der Analysen: {{numberOfAnalyses}}"
|
||||
"due-date": {
|
||||
"label": "Geburtstermin"
|
||||
},
|
||||
"added": {
|
||||
"label": "Datum hinzugefügt: {{added}}"
|
||||
"created-on": {
|
||||
"label": "Erstellt am"
|
||||
},
|
||||
"last-updated": {
|
||||
"label": "Letzte Aktualisierung: {{lastUpdated}}"
|
||||
"project": {
|
||||
"label": "Projekt"
|
||||
},
|
||||
"document": {
|
||||
"label": "Dokument"
|
||||
}
|
||||
},
|
||||
"project-listing": {
|
||||
"table-header": {
|
||||
"title": {
|
||||
"label": "{{length}} aktive Projekte"
|
||||
},
|
||||
"bulk-select": {
|
||||
"label": "Massenauswahl"
|
||||
},
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
}
|
||||
},
|
||||
"stats": {
|
||||
"analyzed-pages": {
|
||||
"label": "Analysierte Seiten"
|
||||
},
|
||||
"total-people": {
|
||||
"label": "Insgesamt Menschen"
|
||||
},
|
||||
"charts": {
|
||||
"projects": {
|
||||
"label": "Projekte"
|
||||
},
|
||||
"total-documents": {
|
||||
"label": "Dokumente insgesamt"
|
||||
}
|
||||
}
|
||||
},
|
||||
"add-edit-dialog": {
|
||||
"header-new": {
|
||||
"label": "Neues Projekt"
|
||||
},
|
||||
"header-edit": {
|
||||
"label": "Projekt bearbeiten"
|
||||
},
|
||||
"form": {
|
||||
"description": {
|
||||
"label": "Beschreibung"
|
||||
},
|
||||
"name": {
|
||||
"label": "Name"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"save": {
|
||||
"label": "Projekt speichern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"label": "Projekte"
|
||||
},
|
||||
"edit": {
|
||||
"action": {
|
||||
"label": "Projekt bearbeiten"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"action": {
|
||||
"label": "Projekt löschen"
|
||||
},
|
||||
"delete-failed": {
|
||||
"label": "Projekt konnte nicht gelöscht werden: {{projectName}}"
|
||||
}
|
||||
},
|
||||
"add-new": {
|
||||
"label": "Neues Projekt"
|
||||
},
|
||||
"no-projects": {
|
||||
"label": "Sie haben derzeit keine Projekte. Sie können Ihre Arbeit beginnen, indem Sie eine neue erstellen!"
|
||||
},
|
||||
"sorting": {
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
},
|
||||
"alphabetically": {
|
||||
"label": "Alphabetisch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-details": {
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Dateidetails"
|
||||
},
|
||||
"actions": {
|
||||
"download-redaction-report": {
|
||||
"label": "Redaktionsbericht herunterladen"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-details": {
|
||||
"project-team": {
|
||||
"label": "Projektteam"
|
||||
},
|
||||
"charts": {
|
||||
"total-documents": {
|
||||
"label": "Dokumente insgesamt"
|
||||
"dialog": {
|
||||
"title": {
|
||||
"label": "Projekt Details"
|
||||
},
|
||||
"info": {
|
||||
"file-count": {
|
||||
"label": "Anzahl der Dateien: {{fileCount}}"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"download-redaction-report": {
|
||||
"label": "Redaktionsbericht herunterladen"
|
||||
},
|
||||
"reanalyse-project": {
|
||||
"label": "Projekt erneut analysieren"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"label": "Projektübersicht"
|
||||
},
|
||||
"upload-document": {
|
||||
"label": "Dokument hochladen"
|
||||
},
|
||||
"no-project": {
|
||||
"label": "Angefordertes Projekt: {{projectId}} existiert nicht! <a href='/ui/projects'>Zurück zur Projektliste.</a>"
|
||||
}
|
||||
},
|
||||
"file-preview": {
|
||||
"view-toggle": {
|
||||
"label": "Redigierte Ansicht"
|
||||
},
|
||||
"filter-menu": {
|
||||
"label": "Filter",
|
||||
"all": {
|
||||
"label": "Alle"
|
||||
},
|
||||
"none": {
|
||||
"label": "Keiner"
|
||||
},
|
||||
"filter-types": {
|
||||
"label": "Filtertypen"
|
||||
},
|
||||
"hint": {
|
||||
"label": "Hinweis Anmerkung",
|
||||
"hint_only": {
|
||||
"label": "Nur Hinweis"
|
||||
"project-overview": {
|
||||
"table-header": {
|
||||
"title": {
|
||||
"label": "{{length}} Dokumente"
|
||||
},
|
||||
"bulk-select": {
|
||||
"label": "Massenauswahl"
|
||||
},
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
}
|
||||
},
|
||||
"vertebrate": {
|
||||
"label": "Wirbeltier"
|
||||
"table-col-names": {
|
||||
"name": {
|
||||
"label": "Name"
|
||||
},
|
||||
"added-on": {
|
||||
"label": "Hinzugefügt zu"
|
||||
},
|
||||
"needs-work": {
|
||||
"label": "Braucht Arbeit"
|
||||
},
|
||||
"assigned-to": {
|
||||
"label": "Zugewiesen an"
|
||||
},
|
||||
"status": {
|
||||
"label": "Status"
|
||||
}
|
||||
},
|
||||
"names": {
|
||||
"label": "Namen"
|
||||
"sorting": {
|
||||
"recent": {
|
||||
"label": "Kürzlich"
|
||||
},
|
||||
"alphabetically": {
|
||||
"label": "Alphabetisch"
|
||||
},
|
||||
"number-of-pages": {
|
||||
"label": "Seitenzahl"
|
||||
},
|
||||
"number-of-analyses": {
|
||||
"label": "Anzahl der Analysen"
|
||||
}
|
||||
},
|
||||
"upload-error": {
|
||||
"label": "Datei konnte nicht hochgeladen werden: {{name}}"
|
||||
},
|
||||
"delete-file-error": {
|
||||
"label": "Fehler beim Löschen der Datei: {{filename}}"
|
||||
},
|
||||
"reanalyse": {
|
||||
"action": {
|
||||
"label": "Datei erneut analysieren"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"action": {
|
||||
"label": "Datei löschen"
|
||||
}
|
||||
},
|
||||
"file-listing": {
|
||||
"file-entry": {
|
||||
"status": {
|
||||
"label": "Status: {{status}}"
|
||||
},
|
||||
"number-of-pages": {
|
||||
"label": "Anzahl der Seiten: {{numberOfPages}}"
|
||||
},
|
||||
"number-of-analyses": {
|
||||
"label": "Anzahl der Analysen: {{numberOfAnalyses}}"
|
||||
},
|
||||
"added": {
|
||||
"label": "Datum hinzugefügt: {{added}}"
|
||||
},
|
||||
"last-updated": {
|
||||
"label": "Letzte Aktualisierung: {{lastUpdated}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"project-details": {
|
||||
"project-team": {
|
||||
"label": "Projektteam"
|
||||
},
|
||||
"charts": {
|
||||
"total-documents": {
|
||||
"label": "Dokumente insgesamt"
|
||||
}
|
||||
}
|
||||
},
|
||||
"header": {
|
||||
"label": "Projektübersicht"
|
||||
},
|
||||
"upload-document": {
|
||||
"label": "Dokument hochladen"
|
||||
},
|
||||
"no-project": {
|
||||
"label": "Angefordertes Projekt: {{projectId}} existiert nicht! <a href='/ui/projects'>Zurück zur Projektliste.</a>"
|
||||
}
|
||||
},
|
||||
"redaction": {
|
||||
"label": "Redaktion"
|
||||
},
|
||||
"comment": {
|
||||
"label": "Kommentar Annotation"
|
||||
},
|
||||
"suggestion": {
|
||||
"label": "Vorgeschlagene Redaktion"
|
||||
},
|
||||
"ignore": {
|
||||
"label": "Redaktion ignoriert"
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"quick-navigation": {
|
||||
"label": "Schnelle Navigation"
|
||||
},
|
||||
"annotations": {
|
||||
"label": "Anmerkungen"
|
||||
},
|
||||
"info": {
|
||||
"label": "Die Info",
|
||||
"assign-reviewer": {
|
||||
"label": "Prüfer zuweisen"
|
||||
"file-preview": {
|
||||
"view-toggle": {
|
||||
"label": "Redigierte Ansicht"
|
||||
},
|
||||
"added-on": {
|
||||
"label": "Hinzugefügt zu"
|
||||
"filter-menu": {
|
||||
"label": "Filter",
|
||||
"all": {
|
||||
"label": "Alle"
|
||||
},
|
||||
"none": {
|
||||
"label": "Keiner"
|
||||
},
|
||||
"filter-types": {
|
||||
"label": "Filtertypen"
|
||||
},
|
||||
"hint": {
|
||||
"label": "Hinweis Anmerkung",
|
||||
"hint_only": {
|
||||
"label": "Nur Hinweis"
|
||||
},
|
||||
"vertebrate": {
|
||||
"label": "Wirbeltier"
|
||||
},
|
||||
"names": {
|
||||
"label": "Namen"
|
||||
}
|
||||
},
|
||||
"redaction": {
|
||||
"label": "Redaktion"
|
||||
},
|
||||
"comment": {
|
||||
"label": "Kommentar Annotation"
|
||||
},
|
||||
"suggestion": {
|
||||
"label": "Vorgeschlagene Redaktion"
|
||||
},
|
||||
"ignore": {
|
||||
"label": "Redaktion ignoriert"
|
||||
}
|
||||
},
|
||||
"added-by": {
|
||||
"label": "Hinzugefügt von"
|
||||
"tabs": {
|
||||
"quick-navigation": {
|
||||
"label": "Schnelle Navigation"
|
||||
},
|
||||
"annotations": {
|
||||
"label": "Anmerkungen"
|
||||
},
|
||||
"info": {
|
||||
"label": "Die Info",
|
||||
"assign-reviewer": {
|
||||
"label": "Prüfer zuweisen"
|
||||
},
|
||||
"added-on": {
|
||||
"label": "Hinzugefügt zu"
|
||||
},
|
||||
"added-by": {
|
||||
"label": "Hinzugefügt von"
|
||||
},
|
||||
"last-modified-on": {
|
||||
"label": "Zuletzt geändert am"
|
||||
}
|
||||
}
|
||||
},
|
||||
"last-modified-on": {
|
||||
"label": "Zuletzt geändert am"
|
||||
"download": {
|
||||
"label": "Herunterladen",
|
||||
"dropdown": {
|
||||
"original": {
|
||||
"label": "Original"
|
||||
},
|
||||
"annotated": {
|
||||
"label": "Kommentiert"
|
||||
},
|
||||
"redacted": {
|
||||
"label": "Redigiert"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"download": {
|
||||
"label": "Herunterladen",
|
||||
"dropdown": {
|
||||
"original": {
|
||||
"label": "Original"
|
||||
},
|
||||
"annotated": {
|
||||
"label": "Kommentiert"
|
||||
},
|
||||
"redacted": {
|
||||
"label": "Redigiert"
|
||||
"initials-avatar": {
|
||||
"unassigned": {
|
||||
"label": "Nicht zugewiesen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"initials-avatar": {
|
||||
"unassigned": {
|
||||
"label": "Nicht zugewiesen"
|
||||
}
|
||||
},
|
||||
"unassigned": "Nicht zugewiesen",
|
||||
"under-review": "Wird überprüft",
|
||||
"under-approval": "In Genehmigung",
|
||||
"efsa": "EFSA-Zulassung",
|
||||
"finished": "Fertig",
|
||||
"approved": "Genehmigt",
|
||||
"submitted": "Eingereicht",
|
||||
"active": "Aktiv",
|
||||
"archived": "Archiviert",
|
||||
"hint": "Hinweis",
|
||||
"ignore": "Ignorieren",
|
||||
"redaction": "Redaktion",
|
||||
"comment": "Kommentar",
|
||||
"suggestion": "Redaktionsvorschlag",
|
||||
"dictionary": "Wörterbuch",
|
||||
"content": "Inhalt",
|
||||
"page": "Seite"
|
||||
},
|
||||
"unassigned": "Nicht zugewiesen",
|
||||
"under-review": "Wird überprüft",
|
||||
"under-approval": "In Genehmigung",
|
||||
"efsa": "EFSA-Zulassung",
|
||||
"finished": "Fertig",
|
||||
"approved": "Genehmigt",
|
||||
"submitted": "Eingereicht",
|
||||
"active": "Aktiv",
|
||||
"archived": "Archiviert",
|
||||
"hint": "Hinweis",
|
||||
"ignore": "Ignorieren",
|
||||
"redaction": "Redaktion",
|
||||
"comment": "Kommentar",
|
||||
"suggestion": "Redaktionsvorschlag",
|
||||
"dictionary": "Wörterbuch",
|
||||
"content": "Inhalt",
|
||||
"page": "Seite"
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
parent.postMessage(location.href, location.origin);
|
||||
</script>
|
||||
</body>
|
||||
<body>
|
||||
<script>
|
||||
parent.postMessage(location.href, location.origin);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,41 +1,45 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.mat-button, .mat-flat-button {
|
||||
font-family: Inter, sans-serif !important;
|
||||
border-radius: 17px !important;
|
||||
font-size: 13px !important;
|
||||
height: 34px;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
||||
&.mat-primary {
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
&:not(.mat-primary) {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.mat-button-wrapper {
|
||||
display: flex;
|
||||
.mat-button,
|
||||
.mat-flat-button {
|
||||
font-family: Inter, sans-serif !important;
|
||||
border-radius: 17px !important;
|
||||
font-size: 13px !important;
|
||||
height: 34px;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
||||
&.mat-primary {
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
&:not(.mat-primary) {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.mat-button-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-button[aria-expanded='true'],
|
||||
.mat-button.overlay {
|
||||
background: rgba($primary, 0.1);
|
||||
}
|
||||
|
||||
.mat-button,
|
||||
.mat-flat-button,
|
||||
.mat-icon-button {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-button[aria-expanded="true"], .mat-button.overlay {
|
||||
background: rgba($primary, 0.1);
|
||||
}
|
||||
|
||||
.mat-button, .mat-flat-button, .mat-icon-button {
|
||||
gap: 6px;
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow-button mat-icon {
|
||||
.arrow-button mat-icon {
|
||||
width: 9px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.mat-checkbox .mat-checkbox-frame {
|
||||
border: 1px solid $grey-5;
|
||||
border: 1px solid $grey-5;
|
||||
}
|
||||
|
||||
.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background, .mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
margin-top: 1px;
|
||||
margin-left: 1px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
.mat-checkbox-indeterminate.mat-accent .mat-checkbox-background,
|
||||
.mat-checkbox-checked.mat-accent .mat-checkbox-background {
|
||||
margin-top: 1px;
|
||||
margin-left: 1px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.mat-checkbox-layout {
|
||||
|
||||
@ -1,70 +1,71 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.oval, .square {
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
|
||||
&.large {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
&.lightgray-dark {
|
||||
background-color: $grey-4;
|
||||
}
|
||||
|
||||
&.lightgray-red {
|
||||
background-color: $grey-4;
|
||||
color: $red-1;
|
||||
}
|
||||
|
||||
&.darkgray-white {
|
||||
background-color: $grey-1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.lightgray-white {
|
||||
background-color: $grey-5;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.red-white {
|
||||
background-color: $red-1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.white-dark {
|
||||
border: 1px solid #E2E4E9;
|
||||
}
|
||||
}
|
||||
|
||||
.oval {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.stats-subtitle {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
.oval,
|
||||
.square {
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
font-size: 10px;
|
||||
line-height: 12px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
border: none;
|
||||
|
||||
gap: 12px;
|
||||
&.large {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
&.lightgray-dark {
|
||||
background-color: $grey-4;
|
||||
}
|
||||
|
||||
&.lightgray-red {
|
||||
background-color: $grey-4;
|
||||
color: $red-1;
|
||||
}
|
||||
|
||||
&.darkgray-white {
|
||||
background-color: $grey-1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.lightgray-white {
|
||||
background-color: $grey-5;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.red-white {
|
||||
background-color: $red-1;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.white-dark {
|
||||
border: 1px solid #e2e4e9;
|
||||
}
|
||||
}
|
||||
|
||||
.oval {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.stats-subtitle {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
gap: 12px;
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,40 +1,38 @@
|
||||
@import "red-variables";
|
||||
|
||||
@import 'red-variables';
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.btn-group-btn {
|
||||
cursor: pointer;
|
||||
color: $accent;
|
||||
background: $white;
|
||||
font-family: Inter, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
padding: 10px 14px;
|
||||
transition: color 0.25s ease-in-out;
|
||||
outline: none;
|
||||
border: none;
|
||||
.btn-group-btn {
|
||||
cursor: pointer;
|
||||
color: $accent;
|
||||
background: $white;
|
||||
font-family: Inter, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
padding: 10px 14px;
|
||||
transition: color 0.25s ease-in-out;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
color: $black;
|
||||
&:hover {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $light;
|
||||
background: $primary;
|
||||
border-radius: 17px;
|
||||
}
|
||||
|
||||
&.active:hover {
|
||||
color: $grey-3;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $light;
|
||||
background: $primary;
|
||||
border-radius: 17px;
|
||||
}
|
||||
|
||||
&.active:hover {
|
||||
color: $grey-3;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.icon-10 {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
@ -3,33 +3,33 @@
|
||||
}
|
||||
|
||||
.dialog {
|
||||
position: relative;
|
||||
min-height: 80px;
|
||||
position: relative;
|
||||
min-height: 80px;
|
||||
|
||||
.dialog-close {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
right: -10px;
|
||||
.dialog-close {
|
||||
position: absolute;
|
||||
top: -15px;
|
||||
right: -10px;
|
||||
|
||||
mat-icon {
|
||||
width: 12px;
|
||||
mat-icon {
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
padding-top: 12px;
|
||||
display: flex;
|
||||
> * {
|
||||
margin-right: 16px
|
||||
.dialog-header {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
padding-top: 12px;
|
||||
display: flex;
|
||||
> * {
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.mat-list-item {
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1 !important;
|
||||
font-size: 13px !important;
|
||||
line-height: 16px !important;
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1 !important;
|
||||
font-size: 13px !important;
|
||||
line-height: 16px !important;
|
||||
}
|
||||
|
||||
.list-50vh {
|
||||
overflow-y: scroll;
|
||||
max-height: 50vh;
|
||||
overflow-y: scroll;
|
||||
max-height: 50vh;
|
||||
}
|
||||
|
||||
@ -1,24 +1,24 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.redacto-logo {
|
||||
height: 14px;
|
||||
width: 22px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.line-1 {
|
||||
height: 6px;
|
||||
width: 16px;
|
||||
border-radius: 3px;
|
||||
background-color: $primary;
|
||||
}
|
||||
|
||||
.line-2 {
|
||||
height: 6px;
|
||||
height: 14px;
|
||||
width: 22px;
|
||||
border-radius: 6px;
|
||||
background-color: $primary;
|
||||
}
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
|
||||
.line-1 {
|
||||
height: 6px;
|
||||
width: 16px;
|
||||
border-radius: 3px;
|
||||
background-color: $primary;
|
||||
}
|
||||
|
||||
.line-2 {
|
||||
height: 6px;
|
||||
width: 22px;
|
||||
border-radius: 6px;
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,56 +4,57 @@
|
||||
@include mat-core();
|
||||
|
||||
$primary-palette: (
|
||||
default: $red-1,
|
||||
lighter: lighten($red-1, 30%),
|
||||
darker: darken($red-1, 30%),
|
||||
text: $red-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
default: $red-1,
|
||||
lighter: lighten($red-1, 30%),
|
||||
darker: darken($red-1, 30%),
|
||||
text: $red-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
);
|
||||
|
||||
$secondary-palette: (
|
||||
default: $grey-1,
|
||||
lighter: lighten($grey-1, 30%),
|
||||
darker: darken($grey-1, 30%),
|
||||
text: $grey-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
default: $grey-1,
|
||||
lighter: lighten($grey-1, 30%),
|
||||
darker: darken($grey-1, 30%),
|
||||
text: $grey-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
);
|
||||
|
||||
$red-palette: (
|
||||
default: $red-1,
|
||||
lighter: lighten($red-1, 30%),
|
||||
darker: darken($red-1, 30%),
|
||||
text: $red-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
default: $red-1,
|
||||
lighter: lighten($red-1, 30%),
|
||||
darker: darken($red-1, 30%),
|
||||
text: $red-1,
|
||||
contrast: (
|
||||
default: $light,
|
||||
lighter: $light,
|
||||
darker: $light
|
||||
)
|
||||
);
|
||||
|
||||
$gn-next-primary: mat-palette($primary-palette, default, lighter, darker, text);
|
||||
$gn-next-secondary: mat-palette($secondary-palette, default, lighter, darker, text);
|
||||
$gn-next-warning: mat-palette($red-palette, default, lighter, darker, text);
|
||||
|
||||
$gn-next-mat-theme: mat-light-theme((
|
||||
color: (
|
||||
primary: $gn-next-primary,
|
||||
accent: $gn-next-secondary,
|
||||
warn: $gn-next-warning,
|
||||
)
|
||||
));
|
||||
$gn-next-mat-theme: mat-light-theme(
|
||||
(
|
||||
color: (
|
||||
primary: $gn-next-primary,
|
||||
accent: $gn-next-secondary,
|
||||
warn: $gn-next-warning
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@include angular-material-theme($gn-next-mat-theme);
|
||||
|
||||
|
||||
.mat-flat-button {
|
||||
min-width: unset !important;
|
||||
min-width: unset !important;
|
||||
}
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
@media only screen and (max-width: 800px) {
|
||||
.visible-lg {
|
||||
display: none !important;
|
||||
}
|
||||
.visible-lg {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.visible-lt-lg {
|
||||
display: none !important;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
.visible-lt-lg {
|
||||
display: flex !important;
|
||||
}
|
||||
.visible-lt-lg {
|
||||
display: flex !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
@import 'red-variables';
|
||||
|
||||
@mixin line-clamp($lines) {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: $lines;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: $lines;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin no-scroll-bar {
|
||||
|
||||
@ -1,52 +1,52 @@
|
||||
@import "red-variables";
|
||||
@import 'red-variables';
|
||||
|
||||
.red-select {
|
||||
.mat-select-value-text {
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.mat-form-field-wrapper {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.mat-form-field-infix {
|
||||
padding: 0;
|
||||
border: none;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.mat-select-value {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.mat-select-arrow-wrapper {
|
||||
padding-left: 5px;
|
||||
|
||||
.mat-select-arrow {
|
||||
color: $grey-1 !important;
|
||||
.mat-select-value-text {
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.mat-form-field-wrapper {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.mat-form-field-infix {
|
||||
padding: 0;
|
||||
border: none;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.mat-select-value {
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.mat-select-arrow-wrapper {
|
||||
padding-left: 5px;
|
||||
|
||||
.mat-select-arrow {
|
||||
color: $grey-1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.red-select-panel {
|
||||
border-radius: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
|
||||
.mat-option {
|
||||
background: $white !important;
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
.mat-option {
|
||||
background: $white !important;
|
||||
font-family: Inter, sans-serif;
|
||||
color: $grey-1;
|
||||
font-size: 13px;
|
||||
line-height: 14px;
|
||||
|
||||
&.mat-selected.mat-active {
|
||||
color: $primary;
|
||||
&.mat-selected.mat-active {
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $grey-2 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $grey-2 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,71 +1,71 @@
|
||||
@import "red-variables";
|
||||
@import "red-mixins";
|
||||
@import 'red-variables';
|
||||
@import 'red-mixins';
|
||||
|
||||
a {
|
||||
color: $primary;
|
||||
color: $primary;
|
||||
|
||||
&:hover {
|
||||
color: lighten($primary, 10%)
|
||||
}
|
||||
&:hover {
|
||||
color: lighten($primary, 10%);
|
||||
}
|
||||
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.heading-xl {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 29px;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 29px;
|
||||
}
|
||||
|
||||
.heading-l {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.all-caps-label {
|
||||
text-transform: uppercase;
|
||||
opacity: 0.7;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.7;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.small-label {
|
||||
opacity: 0.7;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
opacity: 0.7;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.medium-label {
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
line-height: 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.clamp-1 {
|
||||
@include line-clamp(1);
|
||||
@include line-clamp(1);
|
||||
}
|
||||
|
||||
.clamp-2 {
|
||||
@include line-clamp(2);
|
||||
@include line-clamp(2);
|
||||
}
|
||||
|
||||
.primary {
|
||||
color: $primary;
|
||||
opacity: 1;
|
||||
color: $primary;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@300;400;500;600;700&display=swap');
|
||||
@import '~ngx-toastr/toastr';
|
||||
@import "red-material-theme";
|
||||
@import "red-page-layout";
|
||||
@import "red-text-styles";
|
||||
@import "red-dialog";
|
||||
@import "red-input";
|
||||
@import "red-button";
|
||||
@import "red-select";
|
||||
@import "red-list";
|
||||
@import "red-checkbox";
|
||||
@import "red-toggle";
|
||||
@import "red-menu";
|
||||
@import "red-media-queries";
|
||||
@import "red-tables";
|
||||
@import "red-components";
|
||||
@import "red-controls";
|
||||
@import "red-logo";
|
||||
@import 'red-material-theme';
|
||||
@import 'red-page-layout';
|
||||
@import 'red-text-styles';
|
||||
@import 'red-dialog';
|
||||
@import 'red-input';
|
||||
@import 'red-button';
|
||||
@import 'red-select';
|
||||
@import 'red-list';
|
||||
@import 'red-checkbox';
|
||||
@import 'red-toggle';
|
||||
@import 'red-menu';
|
||||
@import 'red-media-queries';
|
||||
@import 'red-tables';
|
||||
@import 'red-components';
|
||||
@import 'red-controls';
|
||||
@import 'red-logo';
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
mat-slide-toggle {
|
||||
.mat-slide-toggle-bar {
|
||||
height: 16px !important;
|
||||
width: 30px !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
.mat-slide-toggle-bar {
|
||||
height: 16px !important;
|
||||
width: 30px !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
|
||||
.mat-slide-toggle-thumb-container {
|
||||
top: 2px !important;
|
||||
left: 2px !important;
|
||||
height: 12px !important;
|
||||
width: 12px !important;
|
||||
}
|
||||
.mat-slide-toggle-thumb-container {
|
||||
top: 2px !important;
|
||||
left: 2px !important;
|
||||
height: 12px !important;
|
||||
width: 12px !important;
|
||||
}
|
||||
|
||||
.mat-slide-toggle-thumb {
|
||||
height: 12px !important;
|
||||
width: 12px !important;
|
||||
}
|
||||
.mat-slide-toggle-thumb {
|
||||
height: 12px !important;
|
||||
width: 12px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,33 @@
|
||||
$white: #FFF;
|
||||
$white: #fff;
|
||||
$black: #000;
|
||||
|
||||
$grey-1: #283241;
|
||||
$grey-2: #F4F5F7;
|
||||
$grey-3: #AAACB3;
|
||||
$grey-4: #E2E4E9;
|
||||
$grey-5: #D3D5DA;
|
||||
$grey-6: #F0F1F4;
|
||||
$grey-2: #f4f5f7;
|
||||
$grey-3: #aaacb3;
|
||||
$grey-4: #e2e4e9;
|
||||
$grey-5: #d3d5da;
|
||||
$grey-6: #f0f1f4;
|
||||
|
||||
$blue-1: #4875F7;
|
||||
$blue-2: #48C9F7;
|
||||
$blue-3: #5B97DB;
|
||||
$blue-4: #374C81;
|
||||
$red-1: #DD4D50;
|
||||
$yellow-1: #FFB83B;
|
||||
$yellow-2: #FFFF02;
|
||||
$green-1: #00FF00;
|
||||
$green-2: #5CE594;
|
||||
$orange-1: #FF801A;
|
||||
$blue-1: #4875f7;
|
||||
$blue-2: #48c9f7;
|
||||
$blue-3: #5b97db;
|
||||
$blue-4: #374c81;
|
||||
$red-1: #dd4d50;
|
||||
$yellow-1: #ffb83b;
|
||||
$yellow-2: #ffff02;
|
||||
$green-1: #00ff00;
|
||||
$green-2: #5ce594;
|
||||
$orange-1: #ff801a;
|
||||
|
||||
$primary: $red-1;
|
||||
$accent: $grey-1;
|
||||
$light: $white;
|
||||
$dark: $black;
|
||||
|
||||
$separator: rgba(226,228,233,0.9);
|
||||
$separator: rgba(226, 228, 233, 0.9);
|
||||
|
||||
$right-container-inside-width: 340px;
|
||||
$right-container-padding: 16px;
|
||||
$right-container-width: calc(#{$right-container-inside-width} + 2*#{$right-container-padding} + 1px);
|
||||
$right-container-width: calc(
|
||||
#{$right-container-inside-width} + 2 *#{$right-container-padding} + 1px
|
||||
);
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export const environment = {
|
||||
production: true,
|
||||
production: true
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false,
|
||||
production: false
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Redacto</title>
|
||||
<base href="/"/>
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon"/>
|
||||
<link href="manifest.webmanifest" rel="manifest">
|
||||
<meta content="#1976d2" name="theme-color">
|
||||
</head>
|
||||
<body>
|
||||
<redaction-root></redaction-root>
|
||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Redacto</title>
|
||||
<base href="/" />
|
||||
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||
<link href="favicon.ico" rel="icon" type="image/x-icon" />
|
||||
<link href="manifest.webmanifest" rel="manifest" />
|
||||
<meta content="#1976d2" name="theme-color" />
|
||||
</head>
|
||||
<body>
|
||||
<redaction-root></redaction-root>
|
||||
<noscript>Please enable JavaScript to continue using this application.</noscript>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import {enableProdMode} from '@angular/core';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import {AppModule} from './app/app.module';
|
||||
import {environment} from './environments/environment';
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
.bootstrapModule(AppModule)
|
||||
.catch((err) => console.error(err));
|
||||
|
||||
@ -1,59 +1,59 @@
|
||||
{
|
||||
"name": "red-ui",
|
||||
"short_name": "red-ui",
|
||||
"theme_color": "#1976d2",
|
||||
"background_color": "#fafafa",
|
||||
"display": "standalone",
|
||||
"scope": "./",
|
||||
"start_url": "./",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
]
|
||||
"name": "red-ui",
|
||||
"short_name": "red-ui",
|
||||
"theme_color": "#1976d2",
|
||||
"background_color": "#fafafa",
|
||||
"display": "standalone",
|
||||
"scope": "./",
|
||||
"start_url": "./",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/icons/icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-128x128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-152x152.png",
|
||||
"sizes": "152x152",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
},
|
||||
{
|
||||
"src": "assets/icons/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable any"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@import "./assets/styles/red-theme";
|
||||
@import './assets/styles/red-theme';
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts", "src/polyfills.ts"]
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts", "src/polyfills.ts"]
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
{
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [true, "attribute", "redaction", "camelCase"],
|
||||
"component-selector": [true, "element", "redaction", "kebab-case"]
|
||||
},
|
||||
"linterOptions": {
|
||||
"exclude": ["!**/*"]
|
||||
}
|
||||
"extends": "../../tslint.json",
|
||||
"rules": {
|
||||
"directive-selector": [true, "attribute", "redaction", "camelCase"],
|
||||
"component-selector": [true, "element", "redaction", "kebab-case"]
|
||||
},
|
||||
"linterOptions": {
|
||||
"exclude": ["!**/*"]
|
||||
}
|
||||
}
|
||||
|
||||
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