Compare commits
3 Commits
master
...
release/4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78d0cedd30 | ||
|
|
590e8da0bc | ||
|
|
489dde97d5 |
@ -16,7 +16,6 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"rxjs/no-ignored-subscription": "warn",
|
"rxjs/no-ignored-subscription": "warn",
|
||||||
"@angular-eslint/prefer-standalone": "off",
|
|
||||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||||
"@angular-eslint/no-host-metadata-property": "error",
|
"@angular-eslint/no-host-metadata-property": "error",
|
||||||
"@angular-eslint/no-input-rename": "error",
|
"@angular-eslint/no-input-rename": "error",
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -47,4 +47,3 @@ paligo-styles/style.css*
|
|||||||
|
|
||||||
migrations.json
|
migrations.json
|
||||||
*.iml
|
*.iml
|
||||||
/.nx/
|
|
||||||
|
|||||||
@ -4,60 +4,38 @@ variables:
|
|||||||
PROJECT: red-ui
|
PROJECT: red-ui
|
||||||
DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile'
|
DOCKERFILELOCATION: 'docker/$PROJECT/Dockerfile'
|
||||||
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- project: 'gitlab/gitlab'
|
- project: 'gitlab/gitlab'
|
||||||
ref: 'main'
|
ref: 'main'
|
||||||
file: 'ci-templates/docker_build_nexus_v2.yml'
|
file: 'ci-templates/docker_build_nexus_v2.yml'
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_PIPELINE_SOURCE != "schedule"
|
- if: $CI_PIPELINE_SOURCE != "schedule"
|
||||||
|
|
||||||
sonarqube:
|
|
||||||
stage: test
|
|
||||||
image:
|
|
||||||
name: sonarsource/sonar-scanner-cli:11.1
|
|
||||||
entrypoint:
|
|
||||||
- ''
|
|
||||||
variables:
|
|
||||||
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
|
|
||||||
GIT_DEPTH: '0'
|
|
||||||
cache:
|
|
||||||
key: "${CI_JOB_NAME}"
|
|
||||||
paths:
|
|
||||||
- ".sonar/cache"
|
|
||||||
script:
|
|
||||||
- sonar-scanner
|
|
||||||
rules:
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
||||||
- if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH"
|
|
||||||
- if: "$CI_COMMIT_BRANCH =~ /^release/"
|
|
||||||
|
|
||||||
localazy update:
|
localazy update:
|
||||||
image: node:20.5
|
image: node:20.5
|
||||||
cache:
|
cache:
|
||||||
- key:
|
- key:
|
||||||
files:
|
files:
|
||||||
- yarn.lock
|
- yarn.lock
|
||||||
paths:
|
paths:
|
||||||
- .yarn-cache/
|
- .yarn-cache/
|
||||||
script:
|
script:
|
||||||
# - git config user.email "${CI_EMAIL}"
|
- git config user.email "${CI_EMAIL}"
|
||||||
# - git config user.name "${CI_USERNAME}"
|
- git config user.name "${CI_USERNAME}"
|
||||||
# - git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
- git remote add gitlab_origin https://${CI_USERNAME}:${CI_ACCESS_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
||||||
- git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
- cd tools/localazy
|
||||||
- cd tools/localazy
|
- yarn install --cache-folder .yarn-cache
|
||||||
- yarn install --cache-folder .yarn-cache
|
- yarn start
|
||||||
- yarn start
|
- cd ../..
|
||||||
- cd ../..
|
- git add .
|
||||||
- git add .
|
- |-
|
||||||
- |-
|
CHANGES=$(git status --porcelain | wc -l)
|
||||||
CHANGES=$(git status --porcelain | wc -l)
|
if [ "$CHANGES" -gt "0" ]
|
||||||
if [ "$CHANGES" -gt "0" ]
|
then
|
||||||
then
|
git status
|
||||||
git status
|
git commit -m "push back localazy update"
|
||||||
git commit -m "push back localazy update"
|
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME}
|
||||||
git push gitlab_origin HEAD:${CI_COMMIT_REF_NAME}
|
fi
|
||||||
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
rules:
|
||||||
# git push
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||||
fi
|
|
||||||
rules:
|
|
||||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
|
||||||
|
|||||||
11
README.md
11
README.md
@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
### To Create a new Stack in rancher check [this Wiki page](https://wiki.iqser.com/pages/viewpage.action?spaceKey=RED&title=Work+with+kubectl)
|
### To Create a new Stack in rancher check [this Wiki page](https://wiki.iqser.com/pages/viewpage.action?spaceKey=RED&title=Work+with+kubectl)
|
||||||
|
|
||||||
# Dependencies update guide
|
|
||||||
* When updating @pdftron/webviewer, make sure to change the version also in the angular.json and everywhere where the path to /assets/wv-recources is used
|
|
||||||
* Make sure the keycloak.js version is the same with the keycloak version from helm chart
|
|
||||||
|
|
||||||
## Code style
|
## Code style
|
||||||
* Don't use `setInterval` without calling `clearInterval` in `ngOnDestroy` or in `destroyRef.onDestroy(() => clearInterval(intervalId))`
|
|
||||||
|
* Always use `trackBy` in `*ngFor` loops (see shorthand below)
|
||||||
|
```typescript
|
||||||
|
readonly trackBy = trackByFactory();
|
||||||
|
```
|
||||||
|
* Don't use `setInterval` without calling `clearInterval` in `ngOnDestroy`
|
||||||
* Never call getters in HTML templates
|
* Never call getters in HTML templates
|
||||||
|
|
||||||
## Keycloak Staging Config
|
## Keycloak Staging Config
|
||||||
|
|||||||
25
angular.json
25
angular.json
@ -36,13 +36,12 @@
|
|||||||
"prefix": "redaction",
|
"prefix": "redaction",
|
||||||
"targets": {
|
"targets": {
|
||||||
"build": {
|
"build": {
|
||||||
"builder": "@angular/build:application",
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
"options": {
|
"options": {
|
||||||
"outputPath": {
|
"outputPath": "dist/apps/red-ui",
|
||||||
"base": "dist/apps/red-ui"
|
|
||||||
},
|
|
||||||
"index": "apps/red-ui/src/index.html",
|
"index": "apps/red-ui/src/index.html",
|
||||||
"polyfills": ["apps/red-ui/src/polyfills.ts"],
|
"main": "apps/red-ui/src/main.ts",
|
||||||
|
"polyfills": "apps/red-ui/src/polyfills.ts",
|
||||||
"tsConfig": "tsconfig.json",
|
"tsConfig": "tsconfig.json",
|
||||||
"baseHref": "/ui/",
|
"baseHref": "/ui/",
|
||||||
"assets": [
|
"assets": [
|
||||||
@ -50,7 +49,7 @@
|
|||||||
{
|
{
|
||||||
"glob": "**/*",
|
"glob": "**/*",
|
||||||
"input": "node_modules/@pdftron/webviewer/public/",
|
"input": "node_modules/@pdftron/webviewer/public/",
|
||||||
"output": "/assets/wv-resources/11.1.0/"
|
"output": "/assets/wv-resources/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"glob": "**/*",
|
"glob": "**/*",
|
||||||
@ -73,12 +72,13 @@
|
|||||||
"stylePreprocessorOptions": {
|
"stylePreprocessorOptions": {
|
||||||
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
|
"includePaths": ["./apps/red-ui/src/assets/styles", "./libs/common-ui/src/assets/styles"]
|
||||||
},
|
},
|
||||||
"scripts": ["node_modules/chart.js/auto/auto.cjs"],
|
"scripts": ["node_modules/@pdftron/webviewer/webviewer.min.js", "node_modules/chart.js/dist/chart.js"],
|
||||||
|
"vendorChunk": true,
|
||||||
"extractLicenses": false,
|
"extractLicenses": false,
|
||||||
|
"buildOptimizer": false,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"optimization": false,
|
"optimization": false,
|
||||||
"namedChunks": true,
|
"namedChunks": true
|
||||||
"browser": "apps/red-ui/src/main.ts"
|
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
"production": {
|
"production": {
|
||||||
@ -100,6 +100,8 @@
|
|||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"namedChunks": false,
|
"namedChunks": false,
|
||||||
"extractLicenses": true,
|
"extractLicenses": true,
|
||||||
|
"vendorChunk": false,
|
||||||
|
"buildOptimizer": true,
|
||||||
"budgets": [
|
"budgets": [
|
||||||
{
|
{
|
||||||
"type": "initial",
|
"type": "initial",
|
||||||
@ -112,12 +114,13 @@
|
|||||||
"maximumError": "20kb"
|
"maximumError": "20kb"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"serviceWorker": "ngsw-config.json"
|
"serviceWorker": true,
|
||||||
|
"ngswConfigPath": "ngsw-config.json"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
"builder": "@angular/build:dev-server",
|
"builder": "@angular-devkit/build-angular:dev-server",
|
||||||
"options": {
|
"options": {
|
||||||
"buildTarget": "red-ui:build"
|
"buildTarget": "red-ui:build"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
import { RouteReuseStrategy, RouterModule } from '@angular/router';
|
||||||
import { ifNotLoggedIn } from '@common-ui/tenants/guards/if-not-logged-in.guard';
|
import { ifNotLoggedIn } from '@common-ui/tenants/guards/if-not-logged-in.guard';
|
||||||
|
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||||
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
|
||||||
import { DashboardGuard } from '@guards/dashboard-guard.service';
|
import { DashboardGuard } from '@guards/dashboard-guard.service';
|
||||||
@ -8,7 +9,7 @@ import { DossierFilesGuard } from '@guards/dossier-files-guard';
|
|||||||
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
import { templateExistsWhenEnteringDossierList } from '@guards/dossier-template-exists.guard';
|
||||||
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
|
import { loadActiveDossiersGuard, loadAllDossiersGuard, loadArchivedDossiersGuard } from '@guards/dossiers.guard';
|
||||||
import { isNotEditingFileAttributeGuard } from '@guards/file-attribute.guard';
|
import { editAttributeGuard } from '@guards/edit-attribute.guard';
|
||||||
import { FeaturesGuard } from '@guards/features-guard.service';
|
import { FeaturesGuard } from '@guards/features-guard.service';
|
||||||
import { ifLoggedIn } from '@guards/if-logged-in.guard';
|
import { ifLoggedIn } from '@guards/if-logged-in.guard';
|
||||||
import { TrashGuard } from '@guards/trash.guard';
|
import { TrashGuard } from '@guards/trash.guard';
|
||||||
@ -16,13 +17,12 @@ import { CompositeRouteGuard, DEFAULT_REDIRECT_KEY, IqserPermissionsGuard, Iqser
|
|||||||
import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
|
import { TenantSelectComponent } from '@iqser/common-ui/lib/tenants';
|
||||||
import { doesNotHaveAnyRole, hasAnyRole, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
import { doesNotHaveAnyRole, hasAnyRole, IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
import { CustomRouteReuseStrategy } from '@iqser/common-ui/lib/utils';
|
import { CustomRouteReuseStrategy } from '@iqser/common-ui/lib/utils';
|
||||||
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
import { ARCHIVE_ROUTE, BreadcrumbTypes, DOSSIER_ID, DOSSIER_TEMPLATE_ID, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE, FILE_ID } from '@red/domain';
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { mainGuard } from '@utils/main.guard';
|
import { mainGuard } from '@utils/main.guard';
|
||||||
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
import { webViewerLoadedGuard } from './modules/pdf-viewer/services/webviewer-loaded.guard';
|
||||||
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
|
import { ACTIVE_DOSSIERS_SERVICE } from './tokens';
|
||||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
|
||||||
|
|
||||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||||
{
|
{
|
||||||
@ -38,7 +38,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
{
|
{
|
||||||
path: `:${DOSSIER_ID}`,
|
path: `:${DOSSIER_ID}`,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
canDeactivate: [isNotEditingFileAttributeGuard],
|
canDeactivate: [editAttributeGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [DossierFilesGuard],
|
routeGuards: [DossierFilesGuard],
|
||||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
|
breadcrumbs: [BreadcrumbTypes.dossierTemplate, BreadcrumbTypes.dossier],
|
||||||
@ -49,7 +49,7 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
skeleton: 'dossier',
|
skeleton: 'dossier',
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./modules/dossier-overview/dossier-overview.routes'),
|
loadChildren: () => import('./modules/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
path: `:${DOSSIER_ID}/file/:${FILE_ID}`,
|
||||||
@ -63,12 +63,12 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
redirectTo: '/auth-error',
|
redirectTo: '/auth-error',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./modules/file-preview/file-preview.routes'),
|
loadChildren: () => import('./modules/file-preview/file-preview.module').then(m => m.FilePreviewModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
loadChildren: () => import('./modules/dossiers-listing/dossiers-listing.routes'),
|
loadChildren: () => import('./modules/dossiers-listing/dossiers-listing.module').then(m => m.DossiersListingModule),
|
||||||
data: {
|
data: {
|
||||||
breadcrumbs: [BreadcrumbTypes.dossierTemplate],
|
breadcrumbs: [BreadcrumbTypes.dossierTemplate],
|
||||||
},
|
},
|
||||||
@ -77,10 +77,11 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${ARCHIVE_ROUTE}`,
|
path: `${ARCHIVE_ROUTE}`,
|
||||||
loadChildren: () => import('./modules/archive/archive.routes'),
|
loadChildren: () => import('./modules/archive/archive.module').then(m => m.ArchiveModule),
|
||||||
canActivate: [CompositeRouteGuard, loadArchivedDossiersGuard()],
|
canActivate: [CompositeRouteGuard, webViewerLoadedGuard(), loadArchivedDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [FeaturesGuard],
|
routeGuards: [FeaturesGuard],
|
||||||
|
features: [DOSSIERS_ARCHIVE],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -98,16 +99,16 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'account',
|
path: 'account',
|
||||||
loadChildren: () => import('./modules/account/account.routes'),
|
loadChildren: () => import('./modules/account/account.module').then(m => m.AccountModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: 'admin',
|
||||||
loadChildren: () => import('./modules/admin/admin.routes'),
|
loadChildren: () => import('./modules/admin/admin.module').then(m => m.AdminModule),
|
||||||
canActivate: [RedRoleGuard],
|
canActivate: [RedRoleGuard],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'dashboard',
|
path: 'dashboard',
|
||||||
loadChildren: () => import('./modules/dashboard/dashboard.routes'),
|
loadChildren: () => import('./modules/dashboard/dashboard.module').then(m => m.DashboardModule),
|
||||||
canActivate: [CompositeRouteGuard],
|
canActivate: [CompositeRouteGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard, IqserPermissionsGuard, DossierTemplatesGuard, DashboardGuard],
|
||||||
@ -146,7 +147,7 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'search',
|
path: 'search',
|
||||||
loadComponent: () => import('./modules/search/search-screen/search-screen.component'),
|
loadChildren: () => import('./modules/search/search.module').then(m => m.SearchModule),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard, loadAllDossiersGuard()],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
@ -158,7 +159,7 @@ const mainRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'trash',
|
path: 'trash',
|
||||||
loadChildren: () => import('./modules/trash/trash.routes'),
|
loadChildren: () => import('./modules/trash/trash.module').then(m => m.TrashModule),
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard, TrashGuard],
|
||||||
|
|||||||
@ -4,27 +4,26 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
|||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { AppConfig } from '@red/domain';
|
import { AppConfig } from '@red/domain';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
|
import { filter, map, switchMap, take } from 'rxjs/operators';
|
||||||
import { APP_TYPE_PATHS } from '@common-ui/utils/constants';
|
|
||||||
import { MatIconRegistry } from '@angular/material/icon';
|
|
||||||
import { DomSanitizer } from '@angular/platform-browser';
|
|
||||||
import { TenantsService } from '@common-ui/tenants';
|
|
||||||
|
|
||||||
export function loadCustomTheme(cssFileName: string) {
|
function loadCustomTheme() {
|
||||||
const head = document.getElementsByTagName('head')[0];
|
const cssFileName = getConfig<AppConfig>().THEME;
|
||||||
const link = document.createElement('link');
|
|
||||||
link.id = cssFileName;
|
if (cssFileName) {
|
||||||
link.rel = 'stylesheet';
|
const head = document.getElementsByTagName('head')[0];
|
||||||
link.type = 'text/css';
|
const link = document.createElement('link');
|
||||||
link.href = 'assets/styles/themes/' + cssFileName + '.css';
|
link.id = cssFileName;
|
||||||
link.media = 'all';
|
link.rel = 'stylesheet';
|
||||||
head.appendChild(link);
|
link.type = 'text/css';
|
||||||
|
link.href = 'assets/styles/themes/' + cssFileName + '.css';
|
||||||
|
link.media = 'all';
|
||||||
|
head.appendChild(link);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-root',
|
selector: 'redaction-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor(
|
constructor(
|
||||||
@ -35,12 +34,9 @@ export class AppComponent {
|
|||||||
userPreferenceService: UserPreferenceService,
|
userPreferenceService: UserPreferenceService,
|
||||||
renderer: Renderer2,
|
renderer: Renderer2,
|
||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
private readonly _iconRegistry: MatIconRegistry,
|
|
||||||
private readonly _sanitizer: DomSanitizer,
|
|
||||||
private readonly _tenantsService: TenantsService,
|
|
||||||
) {
|
) {
|
||||||
const config = getConfig<AppConfig>();
|
|
||||||
renderer.addClass(document.body, userPreferenceService.getTheme());
|
renderer.addClass(document.body, userPreferenceService.getTheme());
|
||||||
|
loadCustomTheme();
|
||||||
|
|
||||||
const removeQueryParams = _router.events.pipe(
|
const removeQueryParams = _router.events.pipe(
|
||||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||||
@ -51,25 +47,9 @@ export class AppComponent {
|
|||||||
);
|
);
|
||||||
removeQueryParams.subscribe();
|
removeQueryParams.subscribe();
|
||||||
|
|
||||||
this._tenantsService
|
if (getConfig().IS_DOCUMINE) {
|
||||||
.waitForSettingTenant()
|
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
|
||||||
.pipe(
|
}
|
||||||
tap(() => {
|
|
||||||
const isDocumine = this._tenantsService.activeTenant.documine;
|
|
||||||
const logo = isDocumine ? 'documine' : 'redaction';
|
|
||||||
_iconRegistry.addSvgIconInNamespace(
|
|
||||||
'iqser',
|
|
||||||
'logo',
|
|
||||||
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}-logo.svg`),
|
|
||||||
);
|
|
||||||
if (isDocumine) {
|
|
||||||
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
|
|
||||||
}
|
|
||||||
loadCustomTheme(isDocumine ? APP_TYPE_PATHS.SCM : APP_TYPE_PATHS.REDACT);
|
|
||||||
}),
|
|
||||||
take(1),
|
|
||||||
)
|
|
||||||
.subscribe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#removeKeycloakQueryParams() {
|
#removeKeycloakQueryParams() {
|
||||||
|
|||||||
@ -1,19 +1,11 @@
|
|||||||
import { APP_BASE_HREF, DatePipe as BaseDatePipe } from '@angular/common';
|
import { APP_BASE_HREF, DatePipe as BaseDatePipe } from '@angular/common';
|
||||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
import { ErrorHandler, inject, NgModule, provideEnvironmentInitializer } from '@angular/core';
|
import { ENVIRONMENT_INITIALIZER, ErrorHandler, inject, NgModule } from '@angular/core';
|
||||||
import { MatDividerModule } from '@angular/material/divider';
|
import { MatDividerModule } from '@angular/material/divider';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MAT_TOOLTIP_DEFAULT_OPTIONS } from '@angular/material/tooltip';
|
||||||
import { MatMenu, MatMenuContent, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
|
||||||
import { MatProgressSpinner } from '@angular/material/progress-spinner';
|
|
||||||
import { MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltip } from '@angular/material/tooltip';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||||
import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button';
|
|
||||||
import { EmptyStateComponent } from '@common-ui/empty-state';
|
|
||||||
import { HelpModeKey } from '@common-ui/help-mode/types';
|
|
||||||
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
|
||||||
import { RoundCheckboxComponent } from '@common-ui/inputs/round-checkbox/round-checkbox.component';
|
|
||||||
import { GET_TENANT_FROM_PATH_FN, UI_ROOT } from '@common-ui/utils';
|
import { GET_TENANT_FROM_PATH_FN, UI_ROOT } from '@common-ui/utils';
|
||||||
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
|
||||||
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
|
||||||
@ -30,49 +22,51 @@ import { UserMenuComponent } from '@components/user-menu/user-menu.component';
|
|||||||
import { environment } from '@environments/environment';
|
import { environment } from '@environments/environment';
|
||||||
import {
|
import {
|
||||||
CachingModule,
|
CachingModule,
|
||||||
|
ChevronButtonComponent,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
HelpButtonComponent,
|
EmptyStateComponent,
|
||||||
HelpModeComponent,
|
HelpModeKey,
|
||||||
HiddenActionDirective,
|
HiddenActionDirective,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
|
InputWithActionComponent,
|
||||||
IqserAllowDirective,
|
IqserAllowDirective,
|
||||||
IqserDenyDirective,
|
IqserDenyDirective,
|
||||||
|
IqserHelpModeModule,
|
||||||
IqserListingModule,
|
IqserListingModule,
|
||||||
IqserLoadingModule,
|
IqserLoadingModule,
|
||||||
IqserTranslateModule,
|
IqserTranslateModule,
|
||||||
LanguageService,
|
LanguageService,
|
||||||
MAX_RETRIES_ON_SERVER_ERROR,
|
MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
|
RoundCheckboxComponent,
|
||||||
SERVER_ERROR_SKIP_PATHS,
|
SERVER_ERROR_SKIP_PATHS,
|
||||||
ServerErrorInterceptor,
|
ServerErrorInterceptor,
|
||||||
StopPropagationDirective,
|
StopPropagationDirective,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { CommonUiModule } from '@iqser/common-ui/lib/common-ui.module';
|
import { CommonUiModule } from '@iqser/common-ui/lib/common-ui.module';
|
||||||
import { provideHelpMode } from '@iqser/common-ui/lib/help-mode/utils/help-mode.provider';
|
|
||||||
import { LogoComponent, SkeletonComponent, ToastComponent } from '@iqser/common-ui/lib/shared';
|
import { LogoComponent, SkeletonComponent, ToastComponent } from '@iqser/common-ui/lib/shared';
|
||||||
import { TenantsModule } from '@iqser/common-ui/lib/tenants';
|
import { TenantsModule } from '@iqser/common-ui/lib/tenants';
|
||||||
import { InitialsAvatarComponent, IqserUsersModule } from '@iqser/common-ui/lib/users';
|
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { MissingTranslationHandler } from '@ngx-translate/core';
|
||||||
import { AppConfig, ILoggerConfig } from '@red/domain';
|
import { AppConfig, ILoggerConfig } from '@red/domain';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.service';
|
||||||
import { GlobalErrorHandler } from '@services/global-error-handler.service';
|
import { GlobalErrorHandler } from '@services/global-error-handler.service';
|
||||||
import { LoggerRulesService } from '@services/logger-rules.service';
|
import { LoggerRulesService } from '@services/logger-rules.service';
|
||||||
import { provideCustomDateFormatter } from '@shared/custom-date-formatting.provider';
|
|
||||||
import { NavigateLastDossiersScreenDirective } from '@shared/directives/navigate-last-dossiers-screen.directive';
|
|
||||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { UI_CACHES } from '@utils/constants';
|
import { UI_CACHES } from '@utils/constants';
|
||||||
import { ColorPickerService } from 'ngx-color-picker';
|
import { REDMissingTranslationHandler } from '@utils/missing-translations-handler';
|
||||||
import { LoggerModule, NGXLogger, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_SERVICE } from 'ngx-logger';
|
import { LoggerModule, NGXLogger, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_SERVICE } from 'ngx-logger';
|
||||||
import { ToastrModule } from 'ngx-toastr';
|
import { ToastrModule } from 'ngx-toastr';
|
||||||
import helpModeKeys from '../assets/help-mode/help-mode-keys.json';
|
import * as helpModeKeys from '../assets/help-mode/help-mode-keys.json';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { IconsModule } from './modules/icons/icons.module';
|
|
||||||
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
import { PdfViewerModule } from './modules/pdf-viewer/pdf-viewer.module';
|
||||||
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
|
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE } from './tokens';
|
||||||
|
|
||||||
@ -96,6 +90,8 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
SharedModule,
|
||||||
|
FileUploadDownloadModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
MonacoEditorModule,
|
MonacoEditorModule,
|
||||||
CommonUiModule.forRoot({
|
CommonUiModule.forRoot({
|
||||||
@ -108,6 +104,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
existingRoleGuard: RedRoleGuard,
|
existingRoleGuard: RedRoleGuard,
|
||||||
}),
|
}),
|
||||||
CachingModule.forRoot(UI_CACHES),
|
CachingModule.forRoot(UI_CACHES),
|
||||||
|
IqserHelpModeModule.forRoot(helpModeKeys as HelpModeKey[]),
|
||||||
PdfViewerModule,
|
PdfViewerModule,
|
||||||
ToastrModule.forRoot({
|
ToastrModule.forRoot({
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
@ -117,7 +114,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
resetTimeoutOnDuplicate: true,
|
resetTimeoutOnDuplicate: true,
|
||||||
}),
|
}),
|
||||||
TenantsModule.forRoot(),
|
TenantsModule.forRoot(),
|
||||||
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY }),
|
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY || '/assets/i18n/redact/' }),
|
||||||
IqserLoadingModule.forRoot(),
|
IqserLoadingModule.forRoot(),
|
||||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||||
LoggerModule.forRoot(undefined, {
|
LoggerModule.forRoot(undefined, {
|
||||||
@ -135,20 +132,20 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
features: {
|
features: {
|
||||||
ANNOTATIONS: {
|
ANNOTATIONS: {
|
||||||
color: 'aqua',
|
color: 'aqua',
|
||||||
enabled: false,
|
enabled: true,
|
||||||
level: NgxLoggerLevel.DEBUG,
|
level: NgxLoggerLevel.DEBUG,
|
||||||
},
|
},
|
||||||
FILTERS: {
|
FILTERS: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
TENANTS: {
|
TENANTS: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
ROUTES: {
|
ROUTES: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
PDF: {
|
PDF: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
FILE: {
|
FILE: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -171,9 +168,6 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
DOSSIERS_CHANGES: {
|
DOSSIERS_CHANGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
GUARDS: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as ILoggerConfig,
|
} as ILoggerConfig,
|
||||||
},
|
},
|
||||||
@ -192,20 +186,6 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
MatDividerModule,
|
MatDividerModule,
|
||||||
ChevronButtonComponent,
|
ChevronButtonComponent,
|
||||||
InitialsAvatarComponent,
|
|
||||||
HelpModeComponent,
|
|
||||||
HelpButtonComponent,
|
|
||||||
MatMenuTrigger,
|
|
||||||
MatMenuItem,
|
|
||||||
MatIcon,
|
|
||||||
MatMenu,
|
|
||||||
MatMenuContent,
|
|
||||||
MatTooltip,
|
|
||||||
MatProgressSpinner,
|
|
||||||
IconsModule,
|
|
||||||
NavigateLastDossiersScreenDirective,
|
|
||||||
DatePipe,
|
|
||||||
TranslateModule,
|
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@ -233,10 +213,18 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
provide: ErrorHandler,
|
provide: ErrorHandler,
|
||||||
useClass: GlobalErrorHandler,
|
useClass: GlobalErrorHandler,
|
||||||
},
|
},
|
||||||
provideEnvironmentInitializer(async () => {
|
{
|
||||||
const languageService = inject(LanguageService);
|
provide: ENVIRONMENT_INITIALIZER,
|
||||||
return languageService.setInitialLanguage();
|
multi: true,
|
||||||
}),
|
useValue: async () => {
|
||||||
|
const languageService = inject(LanguageService);
|
||||||
|
return languageService.setInitialLanguage();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: MissingTranslationHandler,
|
||||||
|
useClass: REDMissingTranslationHandler,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
provide: MAX_RETRIES_ON_SERVER_ERROR,
|
provide: MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR,
|
useFactory: () => config.MAX_RETRIES_ON_SERVER_ERROR,
|
||||||
@ -257,14 +245,10 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
|
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
|
||||||
useValue: {
|
useValue: {
|
||||||
disableTooltipInteractivity: true,
|
disableTooltipInteractivity: true,
|
||||||
showDelay: 1,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
BaseDatePipe,
|
BaseDatePipe,
|
||||||
DatePipe,
|
DatePipe,
|
||||||
...provideCustomDateFormatter(),
|
|
||||||
...provideHelpMode(helpModeKeys as HelpModeKey[]),
|
|
||||||
ColorPickerService,
|
|
||||||
],
|
],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,17 +2,17 @@
|
|||||||
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
|
<p *ngIf="!adminName && !adminUrl" class="heading-xl" translate="auth-error.heading"></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="adminName && adminUrl"
|
*ngIf="adminName && adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-name-and-link' | translate: { adminName: adminName, adminUrl: adminUrl }"
|
[innerHTML]="'auth-error.heading-with-name-and-link' | translate : { adminName: adminName, adminUrl: adminUrl }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="adminName && !adminUrl"
|
*ngIf="adminName && !adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-name' | translate: { adminName: adminName }"
|
[innerHTML]="'auth-error.heading-with-name' | translate : { adminName: adminName }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<p
|
<p
|
||||||
*ngIf="!adminName && adminUrl"
|
*ngIf="!adminName && adminUrl"
|
||||||
[innerHTML]="'auth-error.heading-with-link' | translate: { adminName: adminName }"
|
[innerHTML]="'auth-error.heading-with-link' | translate : { adminName: adminName }"
|
||||||
class="heading-xl"
|
class="heading-xl"
|
||||||
></p>
|
></p>
|
||||||
<a (click)="userService.logout()" translate="auth-error.logout"></a>
|
<a (click)="userService.logout()" translate="auth-error.logout"></a>
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import { AppConfig } from '@red/domain';
|
|||||||
selector: 'redaction-auth-error',
|
selector: 'redaction-auth-error',
|
||||||
templateUrl: './auth-error.component.html',
|
templateUrl: './auth-error.component.html',
|
||||||
styleUrls: ['./auth-error.component.scss'],
|
styleUrls: ['./auth-error.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class AuthErrorComponent {
|
export class AuthErrorComponent {
|
||||||
readonly #config = getConfig<AppConfig>();
|
readonly #config = getConfig<AppConfig>();
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
<redaction-breadcrumbs></redaction-breadcrumbs>
|
<redaction-breadcrumbs></redaction-breadcrumbs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a [matTooltip]="'top-bar.navigation-items.back-to-dashboard' | translate" [routerLink]="['/main']" class="logo">
|
<a [matTooltip]="'top-bar.navigation-items.back-to-dashboard' | translate" [routerLink]="['/']" class="logo">
|
||||||
<div [attr.help-mode-key]="'home'" class="actions">
|
<div [attr.help-mode-key]="'home'" class="actions">
|
||||||
<iqser-logo (iqserHiddenAction)="userPreferenceService.toggleDevFeatures()" icon="iqser:logo"></iqser-logo>
|
<iqser-logo (iqserHiddenAction)="userPreferenceService.toggleDevFeatures()" icon="iqser:logo"></iqser-logo>
|
||||||
<div class="app-name">{{ titleService.getTitle() }}</div>
|
<div class="app-name">{{ titleService.getTitle() }}</div>
|
||||||
@ -25,7 +25,7 @@
|
|||||||
[placeholder]="'search.placeholder' | translate"
|
[placeholder]="'search.placeholder' | translate"
|
||||||
></redaction-spotlight-search>
|
></redaction-spotlight-search>
|
||||||
|
|
||||||
<iqser-help-button [dialogButton]="false"></iqser-help-button>
|
<iqser-help-button [attr.help-mode-key]="'help_mode'" id="help-mode-button"></iqser-help-button>
|
||||||
|
|
||||||
<redaction-notifications
|
<redaction-notifications
|
||||||
*ngIf="currentUser.isUser || currentUser.isManager"
|
*ngIf="currentUser.isUser || currentUser.isManager"
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-se
|
|||||||
import { filter, map, startWith } from 'rxjs/operators';
|
import { filter, map, startWith } from 'rxjs/operators';
|
||||||
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
||||||
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
import { BreadcrumbsService } from '@services/breadcrumbs.service';
|
||||||
import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '@red/domain';
|
import { FeaturesService } from '@services/features.service';
|
||||||
|
import { ARCHIVE_ROUTE, DOSSIERS_ARCHIVE, DOSSIERS_ROUTE } from '@red/domain';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
|
import { REDDocumentViewer } from '../../modules/pdf-viewer/services/document-viewer.service';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
@ -20,7 +21,6 @@ const isSearchScreen: (url: string) => boolean = url => url.includes('/search');
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-screen.component.html',
|
templateUrl: './base-screen.component.html',
|
||||||
styleUrls: ['./base-screen.component.scss'],
|
styleUrls: ['./base-screen.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class BaseScreenComponent {
|
export class BaseScreenComponent {
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
@ -36,6 +36,7 @@ export class BaseScreenComponent {
|
|||||||
{
|
{
|
||||||
text: this._translateService.instant('search.active-dossiers'),
|
text: this._translateService.instant('search.active-dossiers'),
|
||||||
icon: 'red:enter',
|
icon: 'red:enter',
|
||||||
|
hide: () => !this._featuresService.isEnabled(DOSSIERS_ARCHIVE),
|
||||||
action: (query): void => this.#search(query, [], true),
|
action: (query): void => this.#search(query, [], true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -57,6 +58,7 @@ export class BaseScreenComponent {
|
|||||||
private readonly _router: Router,
|
private readonly _router: Router,
|
||||||
activatedRoute: ActivatedRoute,
|
activatedRoute: ActivatedRoute,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
|
private readonly _featuresService: FeaturesService,
|
||||||
readonly permissionsService: IqserPermissionsService,
|
readonly permissionsService: IqserPermissionsService,
|
||||||
readonly userService: UserService,
|
readonly userService: UserService,
|
||||||
readonly userPreferenceService: UserPreferenceService,
|
readonly userPreferenceService: UserPreferenceService,
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import { Breadcrumb, BreadcrumbDisplayType, BreadcrumbsService } from '@services
|
|||||||
selector: 'redaction-breadcrumbs',
|
selector: 'redaction-breadcrumbs',
|
||||||
templateUrl: './breadcrumbs.component.html',
|
templateUrl: './breadcrumbs.component.html',
|
||||||
styleUrls: ['./breadcrumbs.component.scss'],
|
styleUrls: ['./breadcrumbs.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class BreadcrumbsComponent {
|
export class BreadcrumbsComponent {
|
||||||
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
|
constructor(readonly breadcrumbsService: BreadcrumbsService) {}
|
||||||
|
|||||||
@ -13,7 +13,6 @@ import { firstValueFrom } from 'rxjs';
|
|||||||
entitiesService: FileDownloadService,
|
entitiesService: FileDownloadService,
|
||||||
component: DownloadsListScreenComponent,
|
component: DownloadsListScreenComponent,
|
||||||
}),
|
}),
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy {
|
export class DownloadsListScreenComponent extends ListingComponent<DownloadStatus> implements OnDestroy {
|
||||||
readonly #interval: number;
|
readonly #interval: number;
|
||||||
|
|||||||
@ -32,18 +32,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
(click)="handleMarkReadEvent($event, [notification], true)"
|
(click)="markRead([notification], true)"
|
||||||
*ngFor="let notification of group.notifications; trackBy: trackBy"
|
*ngFor="let notification of group.notifications; trackBy: trackBy"
|
||||||
[class.unread]="!notification.readDate"
|
[class.unread]="!notification.readDate"
|
||||||
[id]="'notifications-mark-as-read-' + notification.id + '-btn'"
|
[id]="'notifications-mark-as-read-' + notification.id + '-btn'"
|
||||||
class="notification"
|
class="notification"
|
||||||
|
iqserStopPropagation
|
||||||
mat-menu-item
|
mat-menu-item
|
||||||
>
|
>
|
||||||
<iqser-initials-avatar [user]="notification.userId"></iqser-initials-avatar>
|
<iqser-initials-avatar [user]="notification.userId"></iqser-initials-avatar>
|
||||||
|
|
||||||
<div class="notification-content">
|
<div class="notification-content">
|
||||||
<div [innerHTML]="notification.message"></div>
|
<div [innerHTML]="notification.message"></div>
|
||||||
<div class="small-label mt-2">{{ notification.creationDate | date: 'exactDate' }}</div>
|
<div class="small-label mt-2">{{ notification.creationDate | date : 'exactDate' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -51,7 +52,7 @@
|
|||||||
[id]="'notifications-mark-' + notification.id"
|
[id]="'notifications-mark-' + notification.id"
|
||||||
class="dot"
|
class="dot"
|
||||||
iqserStopPropagation
|
iqserStopPropagation
|
||||||
matTooltip="{{ 'notifications.mark-as' | translate: { type: notification.readDate ? 'unread' : 'read' } }}"
|
matTooltip="{{ 'notifications.mark-as' | translate : { type: notification.readDate ? 'unread' : 'read' } }}"
|
||||||
matTooltipPosition="before"
|
matTooltipPosition="before"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mat-mdc-menu-item.notification {
|
.mat-mdc-menu-item.notification {
|
||||||
padding: 8px 26px 10px 8px !important;
|
padding: 8px 26px 10px 8px;
|
||||||
margin: 2px 0 0 0;
|
margin: 2px 0 0 0;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -25,7 +25,6 @@ function chronologically(first: string, second: string) {
|
|||||||
selector: 'redaction-notifications',
|
selector: 'redaction-notifications',
|
||||||
templateUrl: './notifications.component.html',
|
templateUrl: './notifications.component.html',
|
||||||
styleUrls: ['./notifications.component.scss'],
|
styleUrls: ['./notifications.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class NotificationsComponent {
|
export class NotificationsComponent {
|
||||||
readonly hasUnreadNotifications$: Observable<boolean>;
|
readonly hasUnreadNotifications$: Observable<boolean>;
|
||||||
@ -59,15 +58,6 @@ export class NotificationsComponent {
|
|||||||
await this._notificationsService.toggleNotificationRead(notificationsIds, isRead);
|
await this._notificationsService.toggleNotificationRead(notificationsIds, isRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleMarkReadEvent(event: MouseEvent, notifications: Notification[] = this._notificationsService.all, isRead = true) {
|
|
||||||
if (!(event.target as HTMLBaseElement).href) {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.markRead(notifications, isRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
#groupNotifications(notifications: Notification[]): NotificationsGroup[] {
|
#groupNotifications(notifications: Notification[]): NotificationsGroup[] {
|
||||||
const todayTranslation = this._translateService.instant('today');
|
const todayTranslation = this._translateService.instant('today');
|
||||||
const groupedMap = notifications.groupBy(n => {
|
const groupedMap = notifications.groupBy(n => {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
width: 1000px;
|
width: 900px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
templateUrl: './dashboard-skeleton.component.html',
|
templateUrl: './dashboard-skeleton.component.html',
|
||||||
styleUrls: ['./dashboard-skeleton.component.scss'],
|
styleUrls: ['./dashboard-skeleton.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class DashboardSkeletonComponent {}
|
export class DashboardSkeletonComponent {}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } fr
|
|||||||
templateUrl: './dossier-skeleton.component.html',
|
templateUrl: './dossier-skeleton.component.html',
|
||||||
styleUrls: ['./dossier-skeleton.component.scss'],
|
styleUrls: ['./dossier-skeleton.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class DossierSkeletonComponent implements OnInit {
|
export class DossierSkeletonComponent implements OnInit {
|
||||||
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;
|
@ViewChild('workload1', { static: true }) workload1: TemplateRef<unknown>;
|
||||||
|
|||||||
@ -5,6 +5,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
|
|||||||
templateUrl: './skeleton-stats.component.html',
|
templateUrl: './skeleton-stats.component.html',
|
||||||
styleUrls: ['./skeleton-stats.component.scss'],
|
styleUrls: ['./skeleton-stats.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class SkeletonStatsComponent {}
|
export class SkeletonStatsComponent {}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { Title } from '@angular/platform-browser';
|
|||||||
templateUrl: './skeleton-top-bar.component.html',
|
templateUrl: './skeleton-top-bar.component.html',
|
||||||
styleUrls: ['./skeleton-top-bar.component.scss'],
|
styleUrls: ['./skeleton-top-bar.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class SkeletonTopBarComponent {
|
export class SkeletonTopBarComponent {
|
||||||
constructor(readonly titleService: Title) {}
|
constructor(readonly titleService: Title) {}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { MatMenuTrigger } from '@angular/material/menu';
|
|||||||
templateUrl: './spotlight-search.component.html',
|
templateUrl: './spotlight-search.component.html',
|
||||||
styleUrls: ['./spotlight-search.component.scss'],
|
styleUrls: ['./spotlight-search.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class SpotlightSearchComponent {
|
export class SpotlightSearchComponent {
|
||||||
@Input() actions: readonly SpotlightSearchAction[];
|
@Input() actions: readonly SpotlightSearchAction[];
|
||||||
|
|||||||
@ -9,7 +9,6 @@ interface ITenant extends IStoredTenantId {
|
|||||||
selector: 'app-tenants-menu',
|
selector: 'app-tenants-menu',
|
||||||
templateUrl: './tenants-menu.component.html',
|
templateUrl: './tenants-menu.component.html',
|
||||||
styleUrls: ['./tenants-menu.component.scss'],
|
styleUrls: ['./tenants-menu.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class TenantsMenuComponent {
|
export class TenantsMenuComponent {
|
||||||
readonly tenantsService = inject(TenantsService);
|
readonly tenantsService = inject(TenantsService);
|
||||||
|
|||||||
@ -20,7 +20,6 @@ interface MenuItem {
|
|||||||
selector: 'app-user-menu',
|
selector: 'app-user-menu',
|
||||||
templateUrl: './user-menu.component.html',
|
templateUrl: './user-menu.component.html',
|
||||||
styleUrls: ['./user-menu.component.scss'],
|
styleUrls: ['./user-menu.component.scss'],
|
||||||
standalone: false,
|
|
||||||
})
|
})
|
||||||
export class UserMenuComponent {
|
export class UserMenuComponent {
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export class DossierFilesGuard implements CanActivate {
|
|||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
if (!this._dictionaryMapService.has(dossierId) && !this.isDocumine) {
|
if (!this._dictionaryMapService.has(dossierId) && !this.isDocumine) {
|
||||||
const dictionaryPromise = firstValueFrom(this._dictionaryService.loadDictionaryDataForDossier(dossierTemplateId, dossierId));
|
const dictionaryPromise = this._dictionaryService.loadDossierRedaction(dossierTemplateId, dossierId);
|
||||||
promises.push(dictionaryPromise);
|
promises.push(dictionaryPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,35 +7,14 @@ import { DossierTemplateStatsService } from '@services/entity-services/dossier-t
|
|||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
|
||||||
import { WatermarkService } from '@services/entity-services/watermark.service';
|
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
|
||||||
import { getConfig, Toaster } from '@iqser/common-ui';
|
|
||||||
import { RulesService } from '../modules/admin/services/rules.service';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
|
|
||||||
export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
export function templateExistsWhenEnteringAdmin(): CanActivateFn {
|
||||||
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
return async function (route: ActivatedRouteSnapshot): Promise<boolean> {
|
||||||
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
|
||||||
const fileAttributesService = inject(FileAttributesService);
|
|
||||||
const dictionaryService = inject(DictionaryService);
|
|
||||||
const defaultColorsService = inject(DefaultColorsService);
|
|
||||||
const watermarksService = inject(WatermarkService);
|
|
||||||
const router = inject(Router);
|
|
||||||
const rulesService = inject(RulesService);
|
|
||||||
const isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
|
|
||||||
const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId);
|
const dossierTemplate = inject(DossierTemplateStatsService).get(dossierTemplateId);
|
||||||
await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId));
|
|
||||||
await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId));
|
|
||||||
await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId));
|
|
||||||
await firstValueFrom(rulesService.getFor(dossierTemplateId));
|
|
||||||
if (!isDocumine) {
|
|
||||||
await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId));
|
|
||||||
}
|
|
||||||
if (!dossierTemplate) {
|
if (!dossierTemplate) {
|
||||||
await router.navigate(['main', 'admin', 'dossier-templates']);
|
await inject(Router).navigate(['main', 'admin', 'dossier-templates']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -50,16 +29,9 @@ export function templateExistsWhenEnteringDossierList(): CanActivateFn {
|
|||||||
const logger = inject(NGXLogger);
|
const logger = inject(NGXLogger);
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
const userPreferencesService = inject(UserPreferenceService);
|
const userPreferencesService = inject(UserPreferenceService);
|
||||||
const fileAttributesService = inject(FileAttributesService);
|
|
||||||
const dictionaryService = inject(DictionaryService);
|
|
||||||
const defaultColorsService = inject(DefaultColorsService);
|
|
||||||
const watermarksService = inject(WatermarkService);
|
|
||||||
const rulesService = inject(RulesService);
|
|
||||||
const toaster = inject(Toaster);
|
|
||||||
const isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
|
|
||||||
await firstValueFrom(dashboardStatsService.loadForTemplate(dossierTemplateId));
|
await firstValueFrom(dashboardStatsService.loadAll());
|
||||||
await firstValueFrom(dossierTemplatesService.loadDossierTemplate(dossierTemplateId));
|
await firstValueFrom(dossierTemplatesService.loadAll());
|
||||||
const dossierTemplateStats = dashboardStatsService.find(dossierTemplateId);
|
const dossierTemplateStats = dashboardStatsService.find(dossierTemplateId);
|
||||||
if (!dossierTemplateStats || dossierTemplateStats.isEmpty) {
|
if (!dossierTemplateStats || dossierTemplateStats.isEmpty) {
|
||||||
logger.warn(`[ROUTES] Dossier template ${dossierTemplateId} not found, redirecting to main`);
|
logger.warn(`[ROUTES] Dossier template ${dossierTemplateId} not found, redirecting to main`);
|
||||||
@ -67,16 +39,6 @@ export function templateExistsWhenEnteringDossierList(): CanActivateFn {
|
|||||||
await router.navigate(['main']);
|
await router.navigate(['main']);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
await firstValueFrom(fileAttributesService.loadFileAttributesConfig(dossierTemplateId));
|
|
||||||
await firstValueFrom(dictionaryService.loadDictionaryDataForDossierTemplate(dossierTemplateId));
|
|
||||||
await firstValueFrom(defaultColorsService.loadForDossierTemplate(dossierTemplateId));
|
|
||||||
const rules = await firstValueFrom(rulesService.getFor(dossierTemplateId));
|
|
||||||
if (rules.timeoutDetected) {
|
|
||||||
toaster.error(_('dossier-listing.rules.timeoutError'));
|
|
||||||
}
|
|
||||||
if (!isDocumine) {
|
|
||||||
await firstValueFrom(watermarksService.loadForDossierTemplate(dossierTemplateId));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
8
apps/red-ui/src/app/guards/edit-attribute.guard.ts
Normal file
8
apps/red-ui/src/app/guards/edit-attribute.guard.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { CanDeactivateFn } from '@angular/router';
|
||||||
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
|
import { DossierOverviewScreenComponent } from '../modules/dossier-overview/screen/dossier-overview-screen.component';
|
||||||
|
|
||||||
|
// TODO: rename to something like "isNotEditingFileAttributeGuard"
|
||||||
|
export const editAttributeGuard: CanDeactivateFn<DossierOverviewScreenComponent> = () =>
|
||||||
|
!inject(FileAttributesService).isEditingFileAttribute();
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import { inject } from '@angular/core';
|
|
||||||
import { CanDeactivateFn } from '@angular/router';
|
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
|
||||||
import DossierOverviewScreenComponent from '../modules/dossier-overview/screen/dossier-overview-screen.component';
|
|
||||||
|
|
||||||
export const isNotEditingFileAttributeGuard: CanDeactivateFn<DossierOverviewScreenComponent> = () =>
|
|
||||||
!inject(FileAttributesService).isEditingFileAttribute();
|
|
||||||
@ -40,7 +40,8 @@ export function ifLoggedIn(): AsyncGuard {
|
|||||||
|
|
||||||
logger.info('[KEYCLOAK] Keycloak init...');
|
logger.info('[KEYCLOAK] Keycloak init...');
|
||||||
await keycloakInitializer(tenant);
|
await keycloakInitializer(tenant);
|
||||||
logger.info('[KEYCLOAK] Keycloak init done for tenant: ', { tenant });
|
logger.info('[KEYCLOAK] Keycloak init done!');
|
||||||
|
console.log({ tenant });
|
||||||
await tenantsService.selectTenant(tenant);
|
await tenantsService.selectTenant(tenant);
|
||||||
await usersService.initialize();
|
await usersService.initialize();
|
||||||
await licenseService.loadLicenses();
|
await licenseService.loadLicenses();
|
||||||
@ -50,7 +51,6 @@ export function ifLoggedIn(): AsyncGuard {
|
|||||||
const jwtToken = jwtDecode(token) as JwtToken;
|
const jwtToken = jwtDecode(token) as JwtToken;
|
||||||
const authTime = (jwtToken.auth_time || jwtToken.iat).toString();
|
const authTime = (jwtToken.auth_time || jwtToken.iat).toString();
|
||||||
localStorage.setItem('authTime', authTime);
|
localStorage.setItem('authTime', authTime);
|
||||||
localStorage.setItem('token', token);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router';
|
||||||
import { EntityPermissionsService } from '@services/entity-permissions/entity-permissions.service';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { EntityPermissionsService } from '@services/entity-permissions/entity-permissions.service';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class PermissionsGuard implements CanActivate {
|
export class PermissionsGuard implements CanActivate {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { Dictionary, SuperTypes } from '@red/domain';
|
import { Dictionary } from '@red/domain';
|
||||||
|
|
||||||
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) => !isApprover && annotation.pending;
|
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) => !isApprover && annotation.pending;
|
||||||
|
|
||||||
@ -8,15 +8,12 @@ export const canForceHint = (annotation: AnnotationWrapper, canAddRedaction: boo
|
|||||||
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
||||||
|
|
||||||
export const canForceRedaction = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canForceRedaction = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||||
canAddRedaction &&
|
canAddRedaction && annotation.isSkipped && !annotation.isFalsePositive && !annotation.pending;
|
||||||
(annotation.isSkipped || (annotation.IMAGE_HINT && annotation.superType === SuperTypes.Hint)) &&
|
|
||||||
!annotation.isFalsePositive &&
|
|
||||||
!annotation.pending;
|
|
||||||
|
|
||||||
export const canAcceptRecommendation = (annotation: AnnotationWrapper) => annotation.isRecommendation && !annotation.pending;
|
export const canAcceptRecommendation = (annotation: AnnotationWrapper) => annotation.isRecommendation && !annotation.pending;
|
||||||
|
|
||||||
export const canMarkAsFalsePositive = (annotation: AnnotationWrapper, annotationEntity: Dictionary) =>
|
export const canMarkAsFalsePositive = (annotation: AnnotationWrapper, annotationEntity: Dictionary) =>
|
||||||
annotation.canBeMarkedAsFalsePositive && !annotation.hasBeenResizedLocally && annotationEntity?.hasDictionary;
|
annotation.canBeMarkedAsFalsePositive && !annotation.hasBeenForcedRedaction && annotationEntity?.hasDictionary;
|
||||||
|
|
||||||
export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
||||||
canAddRedaction &&
|
canAddRedaction &&
|
||||||
@ -24,13 +21,13 @@ export const canRemoveOnlyHere = (annotation: AnnotationWrapper, canAddRedaction
|
|||||||
(annotation.isRedacted || (annotation.isHint && !annotation.isImage));
|
(annotation.isRedacted || (annotation.isHint && !annotation.isImage));
|
||||||
|
|
||||||
export const canRemoveFromDictionary = (annotation: AnnotationWrapper, autoAnalysisDisabled: boolean) =>
|
export const canRemoveFromDictionary = (annotation: AnnotationWrapper, autoAnalysisDisabled: boolean) =>
|
||||||
(annotation.isModifyDictionary || annotation.engines.includes('DICTIONARY') || annotation.engines.includes('DOSSIER_DICTIONARY')) &&
|
annotation.isModifyDictionary &&
|
||||||
(annotation.isRedacted || annotation.isSkipped || annotation.isHint || (annotation.isIgnoredHint && !annotation.isRuleBased)) &&
|
(annotation.isRedacted || annotation.isSkipped || annotation.isHint) &&
|
||||||
(autoAnalysisDisabled || !annotation.pending) &&
|
(autoAnalysisDisabled || !annotation.pending) &&
|
||||||
annotation.isDictBased;
|
!annotation.hasBeenResized;
|
||||||
|
|
||||||
export const canRemoveRedaction = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
export const canRemoveRedaction = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
||||||
(!annotation.isIgnoredHint || !annotation.isRuleBased) &&
|
!annotation.isIgnoredHint &&
|
||||||
(permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive);
|
(permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive);
|
||||||
|
|
||||||
export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedaction: boolean, autoAnalysisDisabled: boolean) =>
|
||||||
@ -57,17 +54,8 @@ export const canResizeAnnotation = (
|
|||||||
(!annotation.isHint && hasDictionary && annotation.isRuleBased))) ||
|
(!annotation.isHint && hasDictionary && annotation.isRuleBased))) ||
|
||||||
annotation.isRecommendation;
|
annotation.isRecommendation;
|
||||||
|
|
||||||
export const canResizeInDictionary = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
|
||||||
permissions.canResizeAnnotation &&
|
|
||||||
annotation.isModifyDictionary &&
|
|
||||||
!annotation.hasBeenResizedLocally &&
|
|
||||||
!(annotation.hasBeenForcedHint || annotation.hasBeenForcedRedaction);
|
|
||||||
|
|
||||||
export const canEditAnnotation = (annotation: AnnotationWrapper) => (annotation.isRedacted || annotation.isSkipped) && !annotation.isImage;
|
export const canEditAnnotation = (annotation: AnnotationWrapper) => (annotation.isRedacted || annotation.isSkipped) && !annotation.isImage;
|
||||||
|
|
||||||
export const canEditHint = (annotation: AnnotationWrapper) =>
|
export const canEditHint = (annotation: AnnotationWrapper) => (annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint;
|
||||||
((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage;
|
|
||||||
|
|
||||||
export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage;
|
export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage;
|
||||||
|
|
||||||
export const canRevertChanges = (annotation: AnnotationWrapper) => annotation.hasRedactionChanges;
|
|
||||||
|
|||||||
@ -16,8 +16,6 @@ import {
|
|||||||
canRemoveOnlyHere,
|
canRemoveOnlyHere,
|
||||||
canRemoveRedaction,
|
canRemoveRedaction,
|
||||||
canResizeAnnotation,
|
canResizeAnnotation,
|
||||||
canResizeInDictionary,
|
|
||||||
canRevertChanges,
|
|
||||||
canUndo,
|
canUndo,
|
||||||
} from './annotation-permissions.utils';
|
} from './annotation-permissions.utils';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
@ -32,13 +30,11 @@ export class AnnotationPermissions {
|
|||||||
canForceRedaction = true;
|
canForceRedaction = true;
|
||||||
canChangeLegalBasis = true;
|
canChangeLegalBasis = true;
|
||||||
canResizeAnnotation = true;
|
canResizeAnnotation = true;
|
||||||
canResizeInDictionary = true;
|
|
||||||
canRecategorizeAnnotation = true;
|
canRecategorizeAnnotation = true;
|
||||||
canForceHint = true;
|
canForceHint = true;
|
||||||
canEditAnnotations = true;
|
canEditAnnotations = true;
|
||||||
canEditHints = true;
|
canEditHints = true;
|
||||||
canEditImages = true;
|
canEditImages = true;
|
||||||
canRevertChanges = true;
|
|
||||||
|
|
||||||
static forUser(
|
static forUser(
|
||||||
isApprover: boolean,
|
isApprover: boolean,
|
||||||
@ -73,11 +69,9 @@ export class AnnotationPermissions {
|
|||||||
autoAnalysisDisabled,
|
autoAnalysisDisabled,
|
||||||
annotationEntity?.hasDictionary,
|
annotationEntity?.hasDictionary,
|
||||||
);
|
);
|
||||||
permissions.canResizeInDictionary = canResizeInDictionary(annotation, permissions);
|
|
||||||
permissions.canEditAnnotations = canEditAnnotation(annotation);
|
permissions.canEditAnnotations = canEditAnnotation(annotation);
|
||||||
permissions.canEditHints = canEditHint(annotation);
|
permissions.canEditHints = canEditHint(annotation);
|
||||||
permissions.canEditImages = canEditImage(annotation);
|
permissions.canEditImages = canEditImage(annotation);
|
||||||
permissions.canRevertChanges = canRevertChanges(annotation);
|
|
||||||
summedPermissions._merge(permissions);
|
summedPermissions._merge(permissions);
|
||||||
}
|
}
|
||||||
return summedPermissions;
|
return summedPermissions;
|
||||||
@ -86,7 +80,6 @@ export class AnnotationPermissions {
|
|||||||
static reduce(permissions: AnnotationPermissions[]): AnnotationPermissions {
|
static reduce(permissions: AnnotationPermissions[]): AnnotationPermissions {
|
||||||
const result = new AnnotationPermissions();
|
const result = new AnnotationPermissions();
|
||||||
result.canResizeAnnotation = permissions.length === 1 && permissions[0].canResizeAnnotation;
|
result.canResizeAnnotation = permissions.length === 1 && permissions[0].canResizeAnnotation;
|
||||||
result.canResizeInDictionary = permissions.length === 1 && permissions[0].canResizeInDictionary;
|
|
||||||
result.canChangeLegalBasis = permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true);
|
result.canChangeLegalBasis = permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true);
|
||||||
result.canRecategorizeAnnotation = permissions.reduce((acc, next) => acc && next.canRecategorizeAnnotation, true);
|
result.canRecategorizeAnnotation = permissions.reduce((acc, next) => acc && next.canRecategorizeAnnotation, true);
|
||||||
result.canRemoveFromDictionary = permissions.reduce((acc, next) => acc && next.canRemoveFromDictionary, true);
|
result.canRemoveFromDictionary = permissions.reduce((acc, next) => acc && next.canRemoveFromDictionary, true);
|
||||||
@ -100,7 +93,6 @@ export class AnnotationPermissions {
|
|||||||
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
||||||
result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true);
|
result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true);
|
||||||
result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true);
|
result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true);
|
||||||
result.canRevertChanges = permissions.reduce((acc, next) => acc && next.canRevertChanges, true);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,12 +25,7 @@ import {
|
|||||||
SuperTypes,
|
SuperTypes,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
||||||
|
import { log } from '@common-ui/utils';
|
||||||
interface AnnotationContent {
|
|
||||||
translation: string;
|
|
||||||
params: { [key: string]: string };
|
|
||||||
untranslatedContent: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class AnnotationWrapper implements IListable {
|
export class AnnotationWrapper implements IListable {
|
||||||
id: string;
|
id: string;
|
||||||
@ -40,9 +35,9 @@ export class AnnotationWrapper implements IListable {
|
|||||||
typeLabel?: string;
|
typeLabel?: string;
|
||||||
color: string;
|
color: string;
|
||||||
numberOfComments = 0;
|
numberOfComments = 0;
|
||||||
firstBottomLeftPoint: IPoint;
|
firstTopLeftPoint: IPoint;
|
||||||
shortContent: string;
|
shortContent: string;
|
||||||
content: AnnotationContent;
|
content: string;
|
||||||
value: string;
|
value: string;
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
dictionaryOperation = false;
|
dictionaryOperation = false;
|
||||||
@ -63,7 +58,6 @@ export class AnnotationWrapper implements IListable {
|
|||||||
isChangeLogEntry = false;
|
isChangeLogEntry = false;
|
||||||
engines: LogEntryEngine[] = [];
|
engines: LogEntryEngine[] = [];
|
||||||
hasBeenResized: boolean;
|
hasBeenResized: boolean;
|
||||||
hasBeenResizedLocally: boolean;
|
|
||||||
hasBeenRecategorized: boolean;
|
hasBeenRecategorized: boolean;
|
||||||
hasLegalBasisChanged: boolean;
|
hasLegalBasisChanged: boolean;
|
||||||
hasBeenForcedHint: boolean;
|
hasBeenForcedHint: boolean;
|
||||||
@ -71,29 +65,12 @@ export class AnnotationWrapper implements IListable {
|
|||||||
hasBeenRemovedByManualOverride: boolean;
|
hasBeenRemovedByManualOverride: boolean;
|
||||||
isRemoved = false;
|
isRemoved = false;
|
||||||
isRemovedLocally = false;
|
isRemovedLocally = false;
|
||||||
hiddenInWorkload = false;
|
|
||||||
lastManualChange: ManualRedactionType;
|
lastManualChange: ManualRedactionType;
|
||||||
entry: IEntityLogEntry;
|
|
||||||
|
|
||||||
get isRuleBased() {
|
get isRuleBased() {
|
||||||
return this.engines.includes(LogEntryEngines.RULE);
|
return this.engines.includes(LogEntryEngines.RULE);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDictBased() {
|
|
||||||
return [LogEntryEngines.DICTIONARY, LogEntryEngines.DOSSIER_DICTIONARY].some(engine => this.engines.includes(engine));
|
|
||||||
}
|
|
||||||
|
|
||||||
get isRedactedImageHint() {
|
|
||||||
return (
|
|
||||||
(this.IMAGE_HINT && this.superType === SuperTypes.Redaction) ||
|
|
||||||
(this.IMAGE_HINT && this.superType === SuperTypes.ManualRedaction)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSkippedImageHint() {
|
|
||||||
return this.IMAGE_HINT && this.superType === SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
get searchKey(): string {
|
get searchKey(): string {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
@ -112,10 +89,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
|
|
||||||
get canBeMarkedAsFalsePositive() {
|
get canBeMarkedAsFalsePositive() {
|
||||||
return (
|
return (
|
||||||
(this.isRecommendation ||
|
(this.isRecommendation || this.superType === SuperTypes.Redaction) &&
|
||||||
this.superType === SuperTypes.Redaction ||
|
|
||||||
(this.isSkipped && this.isDictBased) ||
|
|
||||||
(this.isRemovedLocally && this.isDictBased)) &&
|
|
||||||
!this.isImage &&
|
!this.isImage &&
|
||||||
!this.imported &&
|
!this.imported &&
|
||||||
!this.pending &&
|
!this.pending &&
|
||||||
@ -199,11 +173,11 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get x() {
|
get x() {
|
||||||
return this.firstBottomLeftPoint.x;
|
return this.firstTopLeftPoint.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
get y() {
|
get y() {
|
||||||
return this.firstBottomLeftPoint.y;
|
return this.firstTopLeftPoint.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
get legalBasis() {
|
get legalBasis() {
|
||||||
@ -228,7 +202,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
annotationWrapper.value = 'Imported';
|
annotationWrapper.value = 'Imported';
|
||||||
annotationWrapper.color = earmark.hexColor;
|
annotationWrapper.color = earmark.hexColor;
|
||||||
annotationWrapper.positions = earmark.positions;
|
annotationWrapper.positions = earmark.positions;
|
||||||
annotationWrapper.firstBottomLeftPoint = earmark.positions[0]?.topLeft;
|
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
|
||||||
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
@ -244,12 +218,11 @@ export class AnnotationWrapper implements IListable {
|
|||||||
) {
|
) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.pending = logEntry.state === EntryStates.PENDING;
|
annotationWrapper.id = logEntry.id;
|
||||||
annotationWrapper.id = logEntry.id + (annotationWrapper.pending ? '-pending' : '');
|
|
||||||
annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
|
annotationWrapper.isChangeLogEntry = logEntry.state === EntryStates.REMOVED || !!changeLogType;
|
||||||
annotationWrapper.type = logEntry.type;
|
annotationWrapper.type = logEntry.type;
|
||||||
annotationWrapper.value = logEntry.value;
|
annotationWrapper.value = logEntry.value;
|
||||||
annotationWrapper.firstBottomLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
|
annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
|
||||||
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
|
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
|
||||||
annotationWrapper.positions = logEntry.positions.map(p => ({
|
annotationWrapper.positions = logEntry.positions.map(p => ({
|
||||||
page: p.pageNumber,
|
page: p.pageNumber,
|
||||||
@ -274,111 +247,84 @@ export class AnnotationWrapper implements IListable {
|
|||||||
annotationWrapper.section = logEntry.section;
|
annotationWrapper.section = logEntry.section;
|
||||||
annotationWrapper.reference = logEntry.reference || [];
|
annotationWrapper.reference = logEntry.reference || [];
|
||||||
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
|
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
|
||||||
annotationWrapper.hasBeenResizedLocally =
|
|
||||||
annotationWrapper.hasBeenResized && annotationWrapper.engines.includes(LogEntryEngines.MANUAL);
|
|
||||||
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
|
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
|
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
|
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
|
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
|
||||||
);
|
);
|
||||||
annotationWrapper.hasBeenForcedHint =
|
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
|
||||||
!!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && annotationWrapper.HINT;
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT,
|
||||||
annotationWrapper.hasBeenForcedRedaction =
|
);
|
||||||
!!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.FORCE) && !annotationWrapper.HINT;
|
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT,
|
||||||
|
);
|
||||||
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
|
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.REMOVE,
|
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY,
|
||||||
);
|
);
|
||||||
|
|
||||||
const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
|
const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
|
||||||
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content.untranslatedContent;
|
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content;
|
||||||
annotationWrapper.content = content;
|
annotationWrapper.content = content;
|
||||||
|
|
||||||
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
|
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
|
||||||
annotationWrapper.lastManualChange = lastRelevantManualChange?.manualRedactionType;
|
annotationWrapper.lastManualChange = lastRelevantManualChange?.manualRedactionType;
|
||||||
|
annotationWrapper.pending = lastRelevantManualChange && !lastRelevantManualChange.processed;
|
||||||
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
|
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
|
||||||
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED;
|
annotationWrapper.isRemoved = logEntry.state === EntryStates.REMOVED;
|
||||||
annotationWrapper.isRemovedLocally =
|
annotationWrapper.isRemovedLocally = lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY;
|
||||||
lastRelevantManualChange?.manualRedactionType === ManualRedactionTypes.REMOVE &&
|
|
||||||
logEntry.engines.includes(LogEntryEngines.MANUAL);
|
|
||||||
|
|
||||||
annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label;
|
annotationWrapper.typeLabel = dictionary?.virtual ? undefined : dictionary?.label;
|
||||||
|
|
||||||
if (annotationWrapper.pending) {
|
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
|
||||||
annotationWrapper.color = defaultColors[annotationDefaultColorConfig.analysis] as string;
|
const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType];
|
||||||
} else {
|
annotationWrapper.color = dictionary ? (dictionary[colorKey] as string) : (defaultColors[defaultColor] as string);
|
||||||
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
|
annotationWrapper['entry'] = logEntry;
|
||||||
const defaultColor = annotationDefaultColorConfig[annotationWrapper.superType];
|
|
||||||
annotationWrapper.color =
|
|
||||||
dictionary && !annotationWrapper.isRedactedImageHint
|
|
||||||
? (dictionary[colorKey] as string)
|
|
||||||
: (defaultColors[defaultColor] as string);
|
|
||||||
}
|
|
||||||
|
|
||||||
annotationWrapper.entry = logEntry;
|
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
|
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
|
||||||
let untranslatedContent = '';
|
let content = '';
|
||||||
const params: { [key: string]: string } = {};
|
|
||||||
if (logEntry.matchedRule) {
|
if (logEntry.matchedRule) {
|
||||||
params['hasRule'] = 'true';
|
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
||||||
params['matchedRule'] = logEntry.matchedRule.replace(/(^[, ]*)|([, ]*$)/g, '');
|
|
||||||
params['ruleSymbol'] = isDocumine ? ':' : '';
|
|
||||||
|
|
||||||
untranslatedContent += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logEntry.reason) {
|
if (logEntry.reason) {
|
||||||
params['hasReason'] = 'true';
|
|
||||||
if (isDocumine && logEntry.reason.slice(-1) === '.') {
|
if (isDocumine && logEntry.reason.slice(-1) === '.') {
|
||||||
logEntry.reason = logEntry.reason.slice(0, -1);
|
logEntry.reason = logEntry.reason.slice(0, -1);
|
||||||
}
|
}
|
||||||
if (!params['hasRule']) {
|
|
||||||
params['reason'] = logEntry.reason.substring(0, 1).toUpperCase() + logEntry.reason.substring(1);
|
content += logEntry.reason + '\n\n';
|
||||||
} else {
|
|
||||||
params['reason'] = logEntry.reason;
|
|
||||||
}
|
|
||||||
params['reason'] = params['reason'].replace(/(^[, ]*)|([, ]*$)/g, '');
|
|
||||||
untranslatedContent += logEntry.reason + '\n\n';
|
|
||||||
//remove leading and trailing commas and whitespaces
|
//remove leading and trailing commas and whitespaces
|
||||||
untranslatedContent = untranslatedContent.replace(/(^[, ]*)|([, ]*$)/g, '');
|
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
untranslatedContent = untranslatedContent.substring(0, 1).toUpperCase() + untranslatedContent.substring(1);
|
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotationWrapper.legalBasis && !isDocumine) {
|
if (annotationWrapper.legalBasis && !isDocumine) {
|
||||||
params['hasLb'] = 'true';
|
content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
|
||||||
params['legalBasis'] = annotationWrapper.legalBasis;
|
|
||||||
untranslatedContent += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (annotationWrapper.hasBeenRemovedByManualOverride) {
|
if (annotationWrapper.hasBeenRemovedByManualOverride) {
|
||||||
params['hasOverride'] = 'true';
|
content += 'Removed by manual override';
|
||||||
untranslatedContent += 'Removed by manual override';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logEntry.section) {
|
if (logEntry.section) {
|
||||||
params['hasSection'] = 'true';
|
|
||||||
params['sectionSymbol'] = isDocumine ? '' : ':';
|
|
||||||
params['shouldLower'] = untranslatedContent.length.toString();
|
|
||||||
params['section'] = logEntry.section;
|
|
||||||
let prefix = `In section${isDocumine ? '' : ':'} `;
|
let prefix = `In section${isDocumine ? '' : ':'} `;
|
||||||
if (untranslatedContent.length) {
|
if (content.length) {
|
||||||
prefix = ` ${prefix.toLowerCase()}`;
|
prefix = ` ${prefix.toLowerCase()}`;
|
||||||
}
|
}
|
||||||
untranslatedContent += `${prefix} "${logEntry.section}"`;
|
content += `${prefix} "${logEntry.section}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { translation: _('annotation-content'), params: params, untranslatedContent: untranslatedContent };
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
|
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
|
||||||
if (annotationWrapper.legalBasis) {
|
if (annotationWrapper.legalBasis) {
|
||||||
const lb = legalBasisList.find(lbm => lbm.technicalName?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
|
const lb = legalBasisList.find(lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
|
||||||
if (lb) {
|
if (lb) {
|
||||||
return lb.name;
|
return lb.name;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||||
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { NotificationPreferencesService } from './services/notification-preferences.service';
|
|
||||||
|
|
||||||
export default [
|
const routes: IqserRoutes = [
|
||||||
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
|
{ path: '', redirectTo: 'user-profile', pathMatch: 'full' },
|
||||||
{
|
{
|
||||||
path: 'user-profile',
|
path: 'user-profile',
|
||||||
@ -16,13 +16,12 @@ export default [
|
|||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
},
|
},
|
||||||
loadChildren: () => import('./screens/user-profile/user-profile.routes'),
|
loadChildren: () => import('./screens/user-profile/user-profile.module').then(m => m.UserProfileModule),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'notifications',
|
path: 'notifications',
|
||||||
component: BaseAccountScreenComponent,
|
component: BaseAccountScreenComponent,
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
data: {
|
data: {
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
permissions: {
|
permissions: {
|
||||||
@ -30,8 +29,7 @@ export default [
|
|||||||
redirectTo: '/',
|
redirectTo: '/',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
providers: [NotificationPreferencesService],
|
loadChildren: () => import('./screens/notifications/notifications.module').then(m => m.NotificationsModule),
|
||||||
loadChildren: () => import('./screens/notifications/notifications.routes'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'preferences',
|
path: 'preferences',
|
||||||
@ -63,4 +61,10 @@ export default [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
] satisfies IqserRoutes;
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AccountRoutingModule {}
|
||||||
@ -4,10 +4,6 @@ import { IqserPermissionsService } from '@iqser/common-ui';
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { SideNavComponent } from '@common-ui/shared';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
readonly label: string;
|
readonly label: string;
|
||||||
@ -21,7 +17,6 @@ interface NavItem {
|
|||||||
templateUrl: './account-side-nav.component.html',
|
templateUrl: './account-side-nav.component.html',
|
||||||
styleUrls: ['./account-side-nav.component.scss'],
|
styleUrls: ['./account-side-nav.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [SideNavComponent, TranslateModule, NgForOf, NgIf, RouterLinkActive, RouterLink],
|
|
||||||
})
|
})
|
||||||
export class AccountSideNavComponent {
|
export class AccountSideNavComponent {
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
|
|||||||
27
apps/red-ui/src/app/modules/account/account.module.ts
Normal file
27
apps/red-ui/src/app/modules/account/account.module.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { AccountRoutingModule } from './account-routing.module';
|
||||||
|
import { AccountSideNavComponent } from './account-side-nav/account-side-nav.component';
|
||||||
|
import { BaseAccountScreenComponent } from './base-account-screen/base-account-screen-component';
|
||||||
|
import { NotificationPreferencesService } from './services/notification-preferences.service';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { IconButtonComponent, IqserAllowDirective, IqserHelpModeModule } from '@iqser/common-ui';
|
||||||
|
import { PreferencesComponent } from './screens/preferences/preferences.component';
|
||||||
|
import { SideNavComponent } from '@iqser/common-ui/lib/shared';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [AccountSideNavComponent, BaseAccountScreenComponent, PreferencesComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
AccountRoutingModule,
|
||||||
|
TranslateModule,
|
||||||
|
IqserHelpModeModule,
|
||||||
|
IconButtonComponent,
|
||||||
|
SideNavComponent,
|
||||||
|
IqserAllowDirective,
|
||||||
|
],
|
||||||
|
providers: [NotificationPreferencesService],
|
||||||
|
})
|
||||||
|
export class AccountModule {}
|
||||||
@ -7,12 +7,11 @@
|
|||||||
<div class="content-inner">
|
<div class="content-inner">
|
||||||
<div class="content-container full-height">
|
<div class="content-container full-height">
|
||||||
<div class="overlay-shadow"></div>
|
<div class="overlay-shadow"></div>
|
||||||
<div [ngClass]="!isWarningsScreen && 'dialog'">
|
<div class="dialog">
|
||||||
@if (!isWarningsScreen) {
|
<div class="dialog-header">
|
||||||
<div class="dialog-header">
|
<div class="heading-l" [translate]="translations[path]"></div>
|
||||||
<div class="heading-l" [translate]="translations[path]"></div>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,35 +1,26 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
import { Router, RouterOutlet } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { accountTranslations } from '@translations/account-translations';
|
import { accountTranslations } from '@translations/account-translations';
|
||||||
import { NgClass } from '@angular/common';
|
|
||||||
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-base-account-screen',
|
selector: 'redaction-base-account-screen',
|
||||||
templateUrl: './base-account-screen-component.html',
|
templateUrl: './base-account-screen-component.html',
|
||||||
styleUrls: ['./base-account-screen-component.scss'],
|
styleUrls: ['./base-account-screen-component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
|
||||||
})
|
})
|
||||||
export class BaseAccountScreenComponent implements OnInit {
|
export class BaseAccountScreenComponent implements OnInit {
|
||||||
readonly translations = accountTranslations;
|
readonly translations = accountTranslations;
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
readonly isWarningsScreen: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(private readonly _router: Router, private readonly _hostRef: ViewContainerRef) {
|
||||||
private readonly _router: Router,
|
|
||||||
private readonly _hostRef: ViewContainerRef,
|
|
||||||
) {
|
|
||||||
this.path = this._router.url.split('/').pop();
|
this.path = this._router.url.split('/').pop();
|
||||||
this.isWarningsScreen = this.path === 'warnings-preferences';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.#setDialogWidth();
|
this._setDialogWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
#setDialogWidth() {
|
private _setDialogWidth() {
|
||||||
const element = this._hostRef.element.nativeElement as HTMLElement;
|
const element = this._hostRef.element.nativeElement as HTMLElement;
|
||||||
element.style.setProperty('--width', this.path === 'user-profile' ? 'unset' : '100%');
|
element.style.setProperty('--width', this.path === 'user-profile' ? 'unset' : '100%');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
|
import { UntypedFormBuilder } from '@angular/forms';
|
||||||
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
import { NotificationPreferencesService } from '../../../services/notification-preferences.service';
|
||||||
import { BaseFormComponent, getConfig, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, getConfig, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
DocumentNotificationsTypes,
|
DocumentNotificationsTypes,
|
||||||
@ -14,10 +14,6 @@ import {
|
|||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
import { notificationsSettingsTranslations } from '@translations/notifications-settings-translations';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
|
|
||||||
const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_REVIEWER', 'ASSIGN_REVIEWER'];
|
const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_REVIEWER', 'ASSIGN_REVIEWER'];
|
||||||
|
|
||||||
@ -25,7 +21,6 @@ const RSS_EXCLUDED_SETTINGS = ['USER_PROMOTED_TO_APPROVER', 'USER_DEGRADED_TO_RE
|
|||||||
templateUrl: './notifications-screen.component.html',
|
templateUrl: './notifications-screen.component.html',
|
||||||
styleUrls: ['./notifications-screen.component.scss'],
|
styleUrls: ['./notifications-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [ReactiveFormsModule, NgForOf, MatSlideToggle, TranslateModule, NgIf, MatCheckbox, IconButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly #toaster = inject(Toaster);
|
readonly #toaster = inject(Toaster);
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { IconButtonComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
|
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [NotificationsScreenComponent],
|
||||||
|
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent],
|
||||||
|
})
|
||||||
|
export class NotificationsModule {}
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { IqserRoutes } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: NotificationsScreenComponent,
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
},
|
|
||||||
] satisfies IqserRoutes;
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
<div class="dialog">
|
|
||||||
<form [formGroup]="form">
|
|
||||||
<div class="dialog-content">
|
|
||||||
<h1>{{ 'dialog-defaults-form.title' | translate }}</h1>
|
|
||||||
<h3>{{ 'dialog-defaults-form.redaction.title' | translate }}</h3>
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label translate="dialog-defaults-form.redaction.add-dialog"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="addRedaction">
|
|
||||||
<mat-option *ngFor="let option of redactionAddOptions" [value]="option.value">{{
|
|
||||||
option.label | translate
|
|
||||||
}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox *ngIf="displayExtraOptionAddRedaction" formControlName="addRedactionApplyToAll">{{
|
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label translate="dialog-defaults-form.redaction.remove-dialog"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="removeRedaction">
|
|
||||||
<mat-option *ngFor="let option of redactionRemoveOptions" [value]="option.value">{{
|
|
||||||
option.label | translate
|
|
||||||
}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveRedaction" formControlName="removeRedactionApplyToAll">{{
|
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<h3>{{ 'dialog-defaults-form.recommendation.title' | translate }}</h3>
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label translate="dialog-defaults-form.recommendation.remove-dialog"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="removeRecommendation">
|
|
||||||
<mat-option *ngFor="let option of recommendationRemoveOptions" [value]="option.value">{{
|
|
||||||
option.label | translate
|
|
||||||
}}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveRecommendation" formControlName="removeRecommendationApplyToAll">{{
|
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<h3>{{ 'dialog-defaults-form.hint.title' | translate }}</h3>
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label translate="dialog-defaults-form.hint.add-dialog"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="addHint">
|
|
||||||
<mat-option *ngFor="let option of hintAddOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox *ngIf="displayExtraOptionAddHint" formControlName="addHintApplyToAll">{{
|
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label translate="dialog-defaults-form.hint.remove-dialog"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="removeHint">
|
|
||||||
<mat-option *ngFor="let option of removeOptions" [value]="option.value">{{ option.label | translate }}</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
<mat-checkbox *ngIf="displayExtraOptionRemoveHint" formControlName="removeHintApplyToAll">{{
|
|
||||||
'dialog-defaults-form.extra-option-label' | translate
|
|
||||||
}}</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="save()"
|
|
||||||
[disabled]="!valid || !changed"
|
|
||||||
[label]="'preferences-screen.actions.save' | translate"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
mat-checkbox {
|
|
||||||
margin: 8px 0 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin-top: 24px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
form .iqser-input-group:not(first-of-type) {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
@ -1,179 +0,0 @@
|
|||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { ChangeDetectorRef, Component, inject } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
|
||||||
import { MatOption, MatSelect } from '@angular/material/select';
|
|
||||||
import { BaseFormComponent } from '@common-ui/form';
|
|
||||||
import { AsControl } from '@common-ui/utils';
|
|
||||||
import { IconButtonComponent } from '@iqser/common-ui';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
|
||||||
import {
|
|
||||||
RedactOrHintOption,
|
|
||||||
RedactOrHintOptions,
|
|
||||||
RemoveRedactionOption,
|
|
||||||
RemoveRedactionOptions,
|
|
||||||
} from '../../../../file-preview/utils/dialog-types';
|
|
||||||
import {
|
|
||||||
hintAddOptions,
|
|
||||||
recommendationRemoveOptions,
|
|
||||||
redactionAddOptions,
|
|
||||||
redactionRemoveOptions,
|
|
||||||
removeOptions,
|
|
||||||
SystemDefaultType,
|
|
||||||
} from '../../../utils/dialog-defaults';
|
|
||||||
|
|
||||||
interface DefaultOptionsForm {
|
|
||||||
addRedaction: RedactOrHintOption | SystemDefaultType;
|
|
||||||
addHint: RedactOrHintOption | SystemDefaultType;
|
|
||||||
removeRedaction: RemoveRedactionOption | SystemDefaultType;
|
|
||||||
removeRecommendation: RemoveRedactionOption | SystemDefaultType;
|
|
||||||
removeHint: RemoveRedactionOption | SystemDefaultType;
|
|
||||||
addRedactionApplyToAll: boolean;
|
|
||||||
removeRedactionApplyToAll: boolean;
|
|
||||||
removeRecommendationApplyToAll: boolean;
|
|
||||||
addHintApplyToAll: boolean;
|
|
||||||
removeHintApplyToAll: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'redaction-dialog-defaults',
|
|
||||||
templateUrl: './dialog-defaults.component.html',
|
|
||||||
styleUrl: './dialog-defaults.component.scss',
|
|
||||||
imports: [ReactiveFormsModule, TranslateModule, MatFormField, MatSelect, MatOption, NgForOf, MatCheckbox, NgIf, IconButtonComponent],
|
|
||||||
})
|
|
||||||
export class DialogDefaultsComponent extends BaseFormComponent {
|
|
||||||
readonly #formBuilder = inject(FormBuilder);
|
|
||||||
readonly #userPreferences = inject(UserPreferenceService);
|
|
||||||
readonly #changeDetectorRef = inject(ChangeDetectorRef);
|
|
||||||
form: FormGroup<AsControl<DefaultOptionsForm>> = this.#formBuilder.group({
|
|
||||||
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
|
||||||
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
|
||||||
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
|
||||||
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
|
|
||||||
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
|
|
||||||
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
|
|
||||||
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
|
|
||||||
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
|
||||||
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
|
||||||
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
|
||||||
});
|
|
||||||
initialFormValue = this.form.getRawValue();
|
|
||||||
|
|
||||||
readonly redactionAddOptions = redactionAddOptions;
|
|
||||||
readonly hintAddOptions = hintAddOptions;
|
|
||||||
readonly removeOptions = removeOptions;
|
|
||||||
readonly redactionRemoveOptions = redactionRemoveOptions;
|
|
||||||
readonly recommendationRemoveOptions = recommendationRemoveOptions;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionAddRedaction() {
|
|
||||||
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addRedaction.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionAddHint() {
|
|
||||||
return RedactOrHintOptions.IN_DOSSIER === this.form.controls.addHint.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveRedaction() {
|
|
||||||
return (
|
|
||||||
[RemoveRedactionOptions.IN_DOSSIER, RemoveRedactionOptions.FALSE_POSITIVE] as Partial<
|
|
||||||
RemoveRedactionOption | SystemDefaultType
|
|
||||||
>[]
|
|
||||||
).includes(this.form.controls.removeRedaction.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveHint() {
|
|
||||||
return RemoveRedactionOptions.IN_DOSSIER === this.form.controls.removeHint.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayExtraOptionRemoveRecommendation() {
|
|
||||||
return RemoveRedactionOptions.DO_NOT_RECOMMEND === this.form.controls.removeRecommendation.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async save(): Promise<any> {
|
|
||||||
const formValue = this.form.value;
|
|
||||||
|
|
||||||
if (this.initialFormValue.addRedaction !== this.form.controls.addRedaction.value) {
|
|
||||||
await this.#userPreferences.saveAddRedactionDefaultOption(this.form.controls.addRedaction.value);
|
|
||||||
}
|
|
||||||
if (this.initialFormValue.addHint !== this.form.controls.addHint.value) {
|
|
||||||
await this.#userPreferences.saveAddHintDefaultOption(this.form.controls.addHint.value);
|
|
||||||
}
|
|
||||||
if (this.initialFormValue.removeRedaction !== this.form.controls.removeRedaction.value) {
|
|
||||||
await this.#userPreferences.saveRemoveRedactionDefaultOption(this.form.controls.removeRedaction.value);
|
|
||||||
}
|
|
||||||
if (this.initialFormValue.removeRecommendation !== this.form.controls.removeRecommendation.value) {
|
|
||||||
await this.#userPreferences.saveRemoveRecommendationDefaultOption(this.form.controls.removeRecommendation.value);
|
|
||||||
}
|
|
||||||
if (this.initialFormValue.removeHint !== this.form.controls.removeHint.value) {
|
|
||||||
await this.#userPreferences.saveRemoveHintDefaultOption(this.form.controls.removeHint.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayExtraOptionAddRedaction) {
|
|
||||||
if (this.initialFormValue.addRedactionApplyToAll !== this.form.controls.addRedactionApplyToAll.value) {
|
|
||||||
await this.#userPreferences.saveAddRedactionDefaultExtraOption(this.form.controls.addRedactionApplyToAll.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.#userPreferences.saveAddRedactionDefaultExtraOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayExtraOptionAddHint) {
|
|
||||||
if (this.initialFormValue.addHintApplyToAll !== this.form.controls.addHintApplyToAll.value) {
|
|
||||||
await this.#userPreferences.saveAddHintDefaultExtraOption(this.form.controls.addHintApplyToAll.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.#userPreferences.saveAddHintDefaultExtraOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveRedaction) {
|
|
||||||
if (this.initialFormValue.removeRedactionApplyToAll !== this.form.controls.removeRedactionApplyToAll.value) {
|
|
||||||
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption(this.form.controls.removeRedactionApplyToAll.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.#userPreferences.saveRemoveRedactionDefaultExtraOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveHint) {
|
|
||||||
if (this.initialFormValue.removeHintApplyToAll !== this.form.controls.removeHintApplyToAll.value) {
|
|
||||||
await this.#userPreferences.saveRemoveHintDefaultExtraOption(this.form.controls.removeHintApplyToAll.value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.#userPreferences.saveRemoveHintDefaultExtraOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.displayExtraOptionRemoveRecommendation) {
|
|
||||||
if (this.initialFormValue.removeRecommendationApplyToAll !== this.form.controls.removeRecommendationApplyToAll.value) {
|
|
||||||
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption(
|
|
||||||
this.form.controls.removeRecommendationApplyToAll.value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.#userPreferences.saveRemoveRecommendationDefaultExtraOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.#userPreferences.reload();
|
|
||||||
this.#patchValues();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
this.#changeDetectorRef.markForCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
#patchValues() {
|
|
||||||
this.form.patchValue({
|
|
||||||
addRedaction: this.#userPreferences.getAddRedactionDefaultOption(),
|
|
||||||
addHint: this.#userPreferences.getAddHintDefaultOption(),
|
|
||||||
removeRedaction: this.#userPreferences.getRemoveRedactionDefaultOption(),
|
|
||||||
removeRecommendation: this.#userPreferences.getRemoveRecommendationDefaultOption(),
|
|
||||||
removeHint: this.#userPreferences.getRemoveHintDefaultOption(),
|
|
||||||
addRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addRedactionDefaultExtraOption),
|
|
||||||
removeRedactionApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRedactionDefaultExtraOption),
|
|
||||||
removeRecommendationApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeRecommendationDefaultExtraOption),
|
|
||||||
addHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.addHintDefaultExtraOption),
|
|
||||||
removeHintApplyToAll: this.#userPreferences.getBool(PreferencesKeys.removeHintDefaultExtraOption),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,52 +1,45 @@
|
|||||||
<redaction-dialog-defaults *ngIf="currentScreen === screens.WARNING_PREFERENCES && !config.IS_DOCUMINE"></redaction-dialog-defaults>
|
<form [formGroup]="form">
|
||||||
|
<div class="dialog-content">
|
||||||
|
<div *ngIf="currentScreen === screens.WARNING_PREFERENCES" class="content-delimiter"></div>
|
||||||
|
<div class="dialog-content-left">
|
||||||
|
<ng-container *ngIf="currentScreen === screens.PREFERENCES">
|
||||||
|
<div class="iqser-input-group">
|
||||||
|
<mat-slide-toggle color="primary" formControlName="autoExpandFiltersOnActions">
|
||||||
|
{{ 'preferences-screen.form.auto-expand-filters-on-action' | translate }}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div [ngClass]="currentScreen === screens.WARNING_PREFERENCES && 'dialog'">
|
<div *ngIf="config.IS_DOCUMINE" class="iqser-input-group">
|
||||||
<form [formGroup]="form">
|
<mat-slide-toggle color="primary" formControlName="openScmDialogByDefault">
|
||||||
<div class="dialog-content">
|
{{ 'preferences-screen.form.open-structured-view-by-default' | translate }}
|
||||||
<div class="dialog-content-left">
|
</mat-slide-toggle>
|
||||||
<ng-container *ngIf="currentScreen === screens.PREFERENCES">
|
</div>
|
||||||
<div class="iqser-input-group">
|
|
||||||
<mat-slide-toggle color="primary" formControlName="autoExpandFiltersOnActions">
|
|
||||||
{{ 'preferences-screen.form.auto-expand-filters-on-action' | translate }}
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *allow="roles.getTables" class="iqser-input-group">
|
<div *allow="roles.getTables" class="iqser-input-group">
|
||||||
<label [translate]="'preferences-screen.form.table-extraction-type'"></label>
|
<label [translate]="'preferences-screen.form.table-extraction-type'"></label>
|
||||||
<input formControlName="tableExtractionType" />
|
<input formControlName="tableExtractionType" />
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="currentScreen === screens.WARNING_PREFERENCES">
|
<ng-container *ngIf="currentScreen === screens.WARNING_PREFERENCES">
|
||||||
<h1>{{ 'preferences-screen.warnings-subtitle' | translate }}</h1>
|
<p class="warnings-subtitle">{{ 'preferences-screen.warnings-subtitle' | translate }}</p>
|
||||||
<p class="warnings-description">{{ 'preferences-screen.warnings-description' | translate }}</p>
|
<p class="warnings-description">{{ 'preferences-screen.warnings-description' | translate }}</p>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="loadAllAnnotationsWarning">
|
<mat-checkbox color="primary" formControlName="loadAllAnnotationsWarning">
|
||||||
{{ 'preferences-screen.form.load-all-annotations-warning' | translate }}
|
{{ 'preferences-screen.form.load-all-annotations-warning' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group">
|
</ng-container>
|
||||||
<mat-checkbox color="primary" formControlName="helpModeDialog">
|
|
||||||
{{ 'preferences-screen.form.help-mode-dialog' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
<div class="iqser-input-group">
|
|
||||||
<mat-checkbox color="primary" formControlName="overwriteFileOption">
|
|
||||||
{{ 'preferences-screen.form.overwrite-file-option' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[disabled]="!valid || !changed"
|
[disabled]="!valid || !changed"
|
||||||
[label]="'preferences-screen.actions.save' | translate"
|
[label]="'preferences-screen.actions.save' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
|
|||||||
@ -1,3 +1,16 @@
|
|||||||
|
@use 'variables';
|
||||||
|
|
||||||
|
.content-delimiter {
|
||||||
|
border-top: 1px solid var(--iqser-separator);
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warnings-subtitle {
|
||||||
|
font-size: var(--iqser-font-size);
|
||||||
|
color: var(--iqser-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
.warnings-description {
|
.warnings-description {
|
||||||
width: 105%;
|
width: 105%;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,33 +1,18 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import {
|
import { BaseFormComponent, getConfig, IqserPermissionsService, isIqserDevMode, LoadingService } from '@iqser/common-ui';
|
||||||
BaseFormComponent,
|
|
||||||
getConfig,
|
|
||||||
IconButtonComponent,
|
|
||||||
IqserAllowDirective,
|
|
||||||
IqserPermissionsService,
|
|
||||||
isIqserDevMode,
|
|
||||||
KEYS,
|
|
||||||
LoadingService,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { AsControl } from '@iqser/common-ui/lib/utils';
|
import { AsControl } from '@iqser/common-ui/lib/utils';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { DialogDefaultsComponent } from './dialog-defaults/dialog-defaults.component';
|
|
||||||
import { NgClass, NgIf } from '@angular/common';
|
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
|
|
||||||
interface PreferencesForm {
|
interface PreferencesForm {
|
||||||
// preferences
|
// preferences
|
||||||
autoExpandFiltersOnActions: boolean;
|
autoExpandFiltersOnActions: boolean;
|
||||||
|
openScmDialogByDefault: boolean;
|
||||||
tableExtractionType: string;
|
tableExtractionType: string;
|
||||||
// warnings preferences
|
// warnings preferences
|
||||||
loadAllAnnotationsWarning: boolean;
|
loadAllAnnotationsWarning: boolean;
|
||||||
helpModeDialog: boolean;
|
|
||||||
overwriteFileOption: boolean;
|
|
||||||
|
|
||||||
[k: string]: any;
|
[k: string]: any;
|
||||||
}
|
}
|
||||||
@ -44,25 +29,8 @@ const Screens = {
|
|||||||
templateUrl: './preferences.component.html',
|
templateUrl: './preferences.component.html',
|
||||||
styleUrls: ['./preferences.component.scss'],
|
styleUrls: ['./preferences.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
|
||||||
DialogDefaultsComponent,
|
|
||||||
NgClass,
|
|
||||||
NgIf,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
MatSlideToggle,
|
|
||||||
TranslateModule,
|
|
||||||
IqserAllowDirective,
|
|
||||||
MatCheckbox,
|
|
||||||
IconButtonComponent,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly #formBuilder = inject(FormBuilder);
|
|
||||||
readonly #permissionsService = inject(IqserPermissionsService);
|
|
||||||
readonly #changeRef = inject(ChangeDetectorRef);
|
|
||||||
readonly #loadingService = inject(LoadingService);
|
|
||||||
readonly #userPreferenceService = inject(UserPreferenceService);
|
|
||||||
|
|
||||||
readonly form: FormGroup<AsControl<PreferencesForm>>;
|
readonly form: FormGroup<AsControl<PreferencesForm>>;
|
||||||
readonly currentScreen: Screen;
|
readonly currentScreen: Screen;
|
||||||
readonly screens = Screens;
|
readonly screens = Screens;
|
||||||
@ -71,87 +39,75 @@ export class PreferencesComponent extends BaseFormComponent implements OnInit {
|
|||||||
readonly config = getConfig();
|
readonly config = getConfig();
|
||||||
readonly isIqserDevMode = isIqserDevMode();
|
readonly isIqserDevMode = isIqserDevMode();
|
||||||
|
|
||||||
get #isOverwriteFileOptionActive() {
|
constructor(
|
||||||
return !(this.#userPreferenceService.getOverwriteFileOption() === 'undefined');
|
route: ActivatedRoute,
|
||||||
}
|
readonly userPreferenceService: UserPreferenceService,
|
||||||
|
private readonly _formBuilder: FormBuilder,
|
||||||
constructor(route: ActivatedRoute) {
|
private readonly _permissionsService: IqserPermissionsService,
|
||||||
|
private readonly _changeRef: ChangeDetectorRef,
|
||||||
|
private readonly _loadingService: LoadingService,
|
||||||
|
) {
|
||||||
super();
|
super();
|
||||||
this.form = this.#formBuilder.group({
|
this.form = this._formBuilder.group({
|
||||||
// preferences
|
// preferences
|
||||||
autoExpandFiltersOnActions: [this.#userPreferenceService.getAutoExpandFiltersOnActions()],
|
autoExpandFiltersOnActions: [this.userPreferenceService.getAutoExpandFiltersOnActions()],
|
||||||
tableExtractionType: [this.#userPreferenceService.getTableExtractionType()],
|
openScmDialogByDefault: [this.userPreferenceService.getOpenScmDialogByDefault()],
|
||||||
|
tableExtractionType: [this.userPreferenceService.getTableExtractionType()],
|
||||||
// warnings preferences
|
// warnings preferences
|
||||||
loadAllAnnotationsWarning: [this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
|
loadAllAnnotationsWarning: [this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)],
|
||||||
helpModeDialog: [this.#userPreferenceService.getBool(KEYS.helpModeDialog)],
|
|
||||||
overwriteFileOption: [this.#isOverwriteFileOptionActive],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.#permissionsService.has(Roles.managePreferences)) {
|
if (!this._permissionsService.has(Roles.managePreferences)) {
|
||||||
this.form.disable();
|
this.form.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.#permissionsService.has(Roles.getTables)) {
|
if (!this._permissionsService.has(Roles.getTables)) {
|
||||||
this.form.controls.tableExtractionType.disable();
|
this.form.controls.tableExtractionType.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.#isOverwriteFileOptionActive) {
|
|
||||||
this.form.controls.overwriteFileOption.disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this.currentScreen = route.snapshot.data.screen;
|
this.currentScreen = route.snapshot.data.screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.#loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<any> {
|
async save(): Promise<any> {
|
||||||
if (this.form.controls.autoExpandFiltersOnActions.value !== this.#userPreferenceService.getAutoExpandFiltersOnActions()) {
|
if (this.form.controls.autoExpandFiltersOnActions.value !== this.userPreferenceService.getAutoExpandFiltersOnActions()) {
|
||||||
await this.#userPreferenceService.toggleAutoExpandFiltersOnActions();
|
await this.userPreferenceService.toggleAutoExpandFiltersOnActions();
|
||||||
|
}
|
||||||
|
if (this.form.controls.openScmDialogByDefault.value !== this.userPreferenceService.getOpenScmDialogByDefault()) {
|
||||||
|
await this.userPreferenceService.toggleOpenScmDialogByDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.form.controls.tableExtractionType.value !== this.#userPreferenceService.getTableExtractionType()) {
|
if (this.form.controls.tableExtractionType.value !== this.userPreferenceService.getTableExtractionType()) {
|
||||||
await this.#userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
|
await this.userPreferenceService.save(PreferencesKeys.tableExtractionType, this.form.controls.tableExtractionType.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.form.controls.loadAllAnnotationsWarning.value !==
|
this.form.controls.loadAllAnnotationsWarning.value !==
|
||||||
this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
|
this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning)
|
||||||
) {
|
) {
|
||||||
await this.#userPreferenceService.save(
|
await this.userPreferenceService.save(
|
||||||
PreferencesKeys.loadAllAnnotationsWarning,
|
PreferencesKeys.loadAllAnnotationsWarning,
|
||||||
String(this.form.controls.loadAllAnnotationsWarning.value),
|
String(this.form.controls.loadAllAnnotationsWarning.value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.form.controls.helpModeDialog.value !== this.#userPreferenceService.getBool(KEYS.helpModeDialog)) {
|
await this.userPreferenceService.reload();
|
||||||
await this.#userPreferenceService.save(KEYS.helpModeDialog, String(this.form.controls.helpModeDialog.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.form.controls.overwriteFileOption.enabled && !this.form.controls.overwriteFileOption.value) {
|
|
||||||
await this.#userPreferenceService.saveOverwriteFileOption('undefined');
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.#userPreferenceService.reload();
|
|
||||||
this.#patchValues();
|
this.#patchValues();
|
||||||
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
this.#changeRef.markForCheck();
|
this._changeRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
#patchValues() {
|
#patchValues() {
|
||||||
this.form.patchValue({
|
this.form.patchValue({
|
||||||
autoExpandFiltersOnActions: this.#userPreferenceService.getAutoExpandFiltersOnActions(),
|
autoExpandFiltersOnActions: this.userPreferenceService.getAutoExpandFiltersOnActions(),
|
||||||
tableExtractionType: this.#userPreferenceService.getTableExtractionType(),
|
openScmDialogByDefault: this.userPreferenceService.getOpenScmDialogByDefault(),
|
||||||
loadAllAnnotationsWarning: this.#userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
|
tableExtractionType: this.userPreferenceService.getTableExtractionType(),
|
||||||
helpModeDialog: this.#userPreferenceService.getBool(KEYS.helpModeDialog),
|
loadAllAnnotationsWarning: this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning),
|
||||||
overwriteFileOption: this.#isOverwriteFileOptionActive,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!this.#isOverwriteFileOptionActive) {
|
|
||||||
this.form.controls.overwriteFileOption.disable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { AbstractControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { AbstractControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
interface FormType {
|
interface FormType {
|
||||||
password: AbstractControl<string>;
|
password: AbstractControl<string>;
|
||||||
@ -10,7 +9,6 @@ interface FormType {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './confirm-password-dialog.component.html',
|
templateUrl: './confirm-password-dialog.component.html',
|
||||||
imports: [ReactiveFormsModule, IconButtonComponent, TranslateModule, CircleButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class ConfirmPasswordDialogComponent extends BaseDialogComponent {
|
export class ConfirmPasswordDialogComponent extends BaseDialogComponent {
|
||||||
constructor(protected readonly _dialogRef: MatDialogRef<ConfirmPasswordDialogComponent>) {
|
constructor(protected readonly _dialogRef: MatDialogRef<ConfirmPasswordDialogComponent>) {
|
||||||
|
|||||||
@ -16,37 +16,32 @@
|
|||||||
<input formControlName="lastName" name="lastName" type="text" />
|
<input formControlName="lastName" name="lastName" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div *ngIf="devMode" class="iqser-input-group">
|
||||||
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
|
<label [translate]="'top-bar.navigation-items.my-account.children.language.label'"></label>
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="language">
|
<mat-select formControlName="language">
|
||||||
<mat-select-trigger>{{ languageSelectLabel() | translate }}</mat-select-trigger>
|
<mat-option *ngFor="let language of languages" [value]="language">
|
||||||
@for (language of languages; track language) {
|
{{ translations[language] | translate }}
|
||||||
<mat-option [value]="language">
|
</mat-option>
|
||||||
{{ translations[language] | translate }}
|
|
||||||
</mat-option>
|
|
||||||
}
|
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<a (click)="resetPassword()" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a>
|
<a [href]="changePasswordUrl" target="_blank"> {{ 'user-profile-screen.actions.change-password' | translate }}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (devMode) {
|
<div *ngIf="devMode" class="iqser-input-group">
|
||||||
<div class="iqser-input-group">
|
<mat-slide-toggle color="primary" formControlName="darkTheme">
|
||||||
<mat-slide-toggle color="primary" formControlName="darkTheme">
|
{{ 'user-profile-screen.form.dark-theme' | translate }}
|
||||||
{{ 'user-profile-screen.form.dark-theme' | translate }}
|
</mat-slide-toggle>
|
||||||
</mat-slide-toggle>
|
</div>
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
[disabled]="disabled"
|
[disabled]="form.invalid || !(profileChanged || languageChanged || themeChanged)"
|
||||||
[label]="'user-profile-screen.actions.save' | translate"
|
[label]="'user-profile-screen.actions.save' | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
|
|||||||
@ -1,64 +1,31 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit } from '@angular/core';
|
||||||
import { FormGroup, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
|
import { DomSanitizer } from '@angular/platform-browser';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import { BaseFormComponent, getConfig, IqserPermissionsService, LanguageService, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
BaseFormComponent,
|
import { TenantsService } from '@iqser/common-ui/lib/tenants';
|
||||||
IconButtonComponent,
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
IqserPermissionsService,
|
import { AppConfig, IProfile } from '@red/domain';
|
||||||
LanguageService,
|
|
||||||
LoadingService,
|
|
||||||
Toaster,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|
||||||
import { IProfile } from '@red/domain';
|
|
||||||
import { languagesTranslations } from '@translations/languages-translations';
|
import { languagesTranslations } from '@translations/languages-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
|
import { UserProfileDialogService } from '../services/user-profile-dialog.service';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
|
||||||
import { PdfViewer } from '../../../../pdf-viewer/services/pdf-viewer.service';
|
|
||||||
import { formControlToSignal } from '@utils/functions';
|
|
||||||
import { AsControl } from '@common-ui/utils';
|
|
||||||
|
|
||||||
interface UserProfileForm {
|
|
||||||
email: string;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
language: string;
|
|
||||||
darkTheme: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './user-profile-screen.component.html',
|
templateUrl: './user-profile-screen.component.html',
|
||||||
styleUrls: ['./user-profile-screen.component.scss'],
|
styleUrls: ['./user-profile-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
|
||||||
ReactiveFormsModule,
|
|
||||||
MatFormField,
|
|
||||||
MatSelect,
|
|
||||||
MatOption,
|
|
||||||
TranslateModule,
|
|
||||||
MatSlideToggle,
|
|
||||||
IconButtonComponent,
|
|
||||||
MatSelectTrigger,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class UserProfileScreenComponent extends BaseFormComponent {
|
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly form: FormGroup<AsControl<UserProfileForm>> = this.#getForm();
|
#profileModel: IProfile;
|
||||||
initialFormValue = this.form.getRawValue();
|
|
||||||
readonly translations = languagesTranslations;
|
readonly translations = languagesTranslations;
|
||||||
readonly devMode = this._userPreferenceService.isIqserDevMode;
|
readonly devMode = this._userPreferenceService.isIqserDevMode;
|
||||||
|
readonly changePasswordUrl: string;
|
||||||
readonly profileKeys = ['email', 'firstName', 'lastName'];
|
|
||||||
readonly languages = this._translateService.langs;
|
|
||||||
readonly language = formControlToSignal<UserProfileForm['language']>(this.form.controls.language);
|
|
||||||
readonly languageSelectLabel = computed(() => this.translations[this.language()]);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
domSanitizer: DomSanitizer,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _dialogService: UserProfileDialogService,
|
private readonly _dialogService: UserProfileDialogService,
|
||||||
@ -69,29 +36,46 @@ export class UserProfileScreenComponent extends BaseFormComponent {
|
|||||||
protected readonly _userPreferenceService: UserPreferenceService,
|
protected readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _changeRef: ChangeDetectorRef,
|
private readonly _changeRef: ChangeDetectorRef,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _pdfViewer: PdfViewer,
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
if (!this._permissionsService.has(Roles.updateMyProfile)) {
|
this._loadingService.start();
|
||||||
this.form.disable();
|
const tenant = inject(TenantsService).activeTenantId;
|
||||||
}
|
const realmUrl = `${getConfig<AppConfig>().OAUTH_URL}/realms/${tenant}`;
|
||||||
this._loadingService.stop();
|
this.changePasswordUrl = `${realmUrl}/account/password`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get languageChanged(): boolean {
|
get languageChanged(): boolean {
|
||||||
return this.initialFormValue['language'] !== this.form.controls.language.value;
|
return this.#profileModel['language'] !== this.form.get('language').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get themeChanged(): boolean {
|
get themeChanged(): boolean {
|
||||||
return this.initialFormValue['darkTheme'] !== this.form.controls.darkTheme.value;
|
return this.#profileModel['darkTheme'] !== this.form.get('darkTheme').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get emailChanged(): boolean {
|
get emailChanged(): boolean {
|
||||||
return this.initialFormValue['email'] !== this.form.controls.email.value;
|
return this.#profileModel['email'] !== this.form.get('email').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get profileChanged(): boolean {
|
get profileChanged(): boolean {
|
||||||
return this.profileKeys.some(key => this.initialFormValue[key] !== this.form.get(key).value);
|
const keys = Object.keys(this.form.getRawValue());
|
||||||
|
keys.splice(keys.indexOf('language'), 1);
|
||||||
|
keys.splice(keys.indexOf('darkTheme'), 1);
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (this.#profileModel[key] !== this.form.get(key).value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
get languages(): string[] {
|
||||||
|
return this._translateService.langs;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this._initializeForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
@ -116,34 +100,50 @@ export class UserProfileScreenComponent extends BaseFormComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.languageChanged) {
|
if (this.languageChanged) {
|
||||||
await this._languageService.change(this.form.controls.language.value);
|
await this._languageService.change(this.form.get('language').value);
|
||||||
await this._pdfViewer.instance?.UI.setLanguage(this._languageService.currentLanguage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.themeChanged) {
|
if (this.themeChanged) {
|
||||||
await this._userPreferenceService.saveTheme(this.form.controls.darkTheme.value ? 'dark' : 'light');
|
await this._userPreferenceService.saveTheme(this.form.get('darkTheme').value ? 'dark' : 'light');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this._initializeForm();
|
||||||
this._changeRef.markForCheck();
|
|
||||||
this._loadingService.stop();
|
|
||||||
this._toaster.success(_('user-profile-screen.update.success'));
|
this._toaster.success(_('user-profile-screen.update.success'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async resetPassword() {
|
private _getForm(): UntypedFormGroup {
|
||||||
await this._userService.createResetPasswordAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm() {
|
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
email: [this._userService.currentUser.email ?? '', [Validators.required, Validators.email]],
|
email: ['', [Validators.required, Validators.email]],
|
||||||
firstName: [this._userService.currentUser.firstName ?? ''],
|
firstName: [''],
|
||||||
lastName: [this._userService.currentUser.lastName ?? ''],
|
lastName: [''],
|
||||||
language: [this._userPreferenceService.getLanguage()],
|
language: [''],
|
||||||
darkTheme: [this._userPreferenceService.getTheme() === 'dark'],
|
darkTheme: [false],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _initializeForm(): void {
|
||||||
|
try {
|
||||||
|
this.form = this._getForm();
|
||||||
|
if (!this._permissionsService.has(Roles.updateMyProfile)) {
|
||||||
|
this.form.disable();
|
||||||
|
}
|
||||||
|
this.#profileModel = {
|
||||||
|
email: this._userService.currentUser.email ?? '',
|
||||||
|
firstName: this._userService.currentUser.firstName ?? '',
|
||||||
|
lastName: this._userService.currentUser.lastName ?? '',
|
||||||
|
language: this._languageService.currentLanguage ?? '',
|
||||||
|
darkTheme: this._userPreferenceService.getTheme() === 'dark',
|
||||||
|
};
|
||||||
|
this.form.patchValue(this.#profileModel, { emitEvent: false });
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
} catch (e) {
|
||||||
|
} finally {
|
||||||
|
this._loadingService.stop();
|
||||||
|
this._changeRef.markForCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { ConfirmPasswordDialogComponent } from './confirm-password-dialog/confirm-password-dialog.component';
|
||||||
|
import { UserProfileDialogService } from './services/user-profile-dialog.service';
|
||||||
|
import { CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
|
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [UserProfileScreenComponent, ConfirmPasswordDialogComponent],
|
||||||
|
imports: [RouterModule.forChild(routes), CommonModule, SharedModule, TranslateModule, IconButtonComponent, CircleButtonComponent],
|
||||||
|
providers: [UserProfileDialogService],
|
||||||
|
})
|
||||||
|
export class UserProfileModule {}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { UserProfileDialogService } from './services/user-profile-dialog.service';
|
|
||||||
import { IqserRoutes } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard], providers: [UserProfileDialogService] },
|
|
||||||
] satisfies IqserRoutes;
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { addHintTranslations } from '@translations/add-hint-translations';
|
|
||||||
import { redactTextTranslations } from '@translations/redact-text-translations';
|
|
||||||
import { removeRedactionTranslations } from '@translations/remove-redaction-translations';
|
|
||||||
import {
|
|
||||||
ForceAnnotationOptions,
|
|
||||||
RectangleRedactOptions,
|
|
||||||
RedactOrHintOptions,
|
|
||||||
RemoveRedactionOptions,
|
|
||||||
} from '../../file-preview/utils/dialog-types';
|
|
||||||
|
|
||||||
export const SystemDefaults = {
|
|
||||||
RECTANGLE_REDACT_DEFAULT: RectangleRedactOptions.ONLY_THIS_PAGE,
|
|
||||||
ADD_REDACTION_DEFAULT: RedactOrHintOptions.IN_DOSSIER,
|
|
||||||
ADD_HINT_DEFAULT: RedactOrHintOptions.IN_DOSSIER,
|
|
||||||
FORCE_REDACTION_DEFAULT: ForceAnnotationOptions.ONLY_HERE,
|
|
||||||
REMOVE_REDACTION_DEFAULT: RemoveRedactionOptions.ONLY_HERE,
|
|
||||||
REMOVE_HINT_DEFAULT: RemoveRedactionOptions.ONLY_HERE,
|
|
||||||
REMOVE_RECOMMENDATION_DEFAULT: RemoveRedactionOptions.DO_NOT_RECOMMEND,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SystemDefaultOption = {
|
|
||||||
SYSTEM_DEFAULT: 'SYSTEM_DEFAULT',
|
|
||||||
label: _('dialog-defaults-form.system-default'),
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type SystemDefaultType = typeof SystemDefaultOption.SYSTEM_DEFAULT;
|
|
||||||
|
|
||||||
export const redactionAddOptions = [
|
|
||||||
{
|
|
||||||
label: SystemDefaultOption.label,
|
|
||||||
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: redactTextTranslations.onlyHere.label,
|
|
||||||
value: RedactOrHintOptions.ONLY_HERE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: redactTextTranslations.inDocument.label,
|
|
||||||
value: RedactOrHintOptions.IN_DOCUMENT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: redactTextTranslations.inDossier.label,
|
|
||||||
value: RedactOrHintOptions.IN_DOSSIER,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const hintAddOptions = [
|
|
||||||
{
|
|
||||||
label: SystemDefaultOption.label,
|
|
||||||
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: addHintTranslations.onlyHere.label,
|
|
||||||
value: RedactOrHintOptions.ONLY_HERE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: addHintTranslations.inDossier.label,
|
|
||||||
value: RedactOrHintOptions.IN_DOSSIER,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const removeOptions = [
|
|
||||||
{
|
|
||||||
label: SystemDefaultOption.label,
|
|
||||||
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: removeRedactionTranslations.ONLY_HERE.label,
|
|
||||||
value: RemoveRedactionOptions.ONLY_HERE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: removeRedactionTranslations.IN_DOSSIER.label,
|
|
||||||
value: RemoveRedactionOptions.IN_DOSSIER,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const redactionRemoveOptions = [
|
|
||||||
...removeOptions,
|
|
||||||
{
|
|
||||||
label: removeRedactionTranslations.FALSE_POSITIVE.label,
|
|
||||||
value: RemoveRedactionOptions.FALSE_POSITIVE,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const recommendationRemoveOptions = [
|
|
||||||
{
|
|
||||||
label: SystemDefaultOption.label,
|
|
||||||
value: SystemDefaultOption.SYSTEM_DEFAULT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: removeRedactionTranslations.DO_NOT_RECOMMEND.label,
|
|
||||||
value: RemoveRedactionOptions.DO_NOT_RECOMMEND,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
282
apps/red-ui/src/app/modules/admin/admin-routing.module.ts
Normal file
282
apps/red-ui/src/app/modules/admin/admin-routing.module.ts
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
||||||
|
import { RedRoleGuard } from '@users/red-role.guard';
|
||||||
|
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
||||||
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||||
|
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
||||||
|
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
||||||
|
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
||||||
|
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
||||||
|
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
||||||
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
|
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||||
|
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
|
||||||
|
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
||||||
|
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||||
|
import { PermissionsGuard } from '@guards/permissions-guard';
|
||||||
|
import { Roles } from '@users/roles';
|
||||||
|
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
||||||
|
|
||||||
|
const dossierTemplateIdRoutes: IqserRoutes = [
|
||||||
|
{
|
||||||
|
path: 'info',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () => import('./screens/info/dossier-template-info.module').then(m => m.DossierTemplateInfoModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'entities',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: EntitiesListingScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `:${ENTITY_TYPE}`,
|
||||||
|
component: BaseEntityScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, entityExistsGuard()],
|
||||||
|
loadChildren: () => import('./screens/entities/entities.module').then(m => m.EntitiesModule),
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'entity-rules',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.rules.read],
|
||||||
|
redirectTo: 'info',
|
||||||
|
},
|
||||||
|
type: 'ENTITY',
|
||||||
|
},
|
||||||
|
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'component-rules',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.rules.read],
|
||||||
|
redirectTo: 'info',
|
||||||
|
},
|
||||||
|
type: 'COMPONENT',
|
||||||
|
},
|
||||||
|
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'file-attributes',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./screens/file-attributes-listing/file-attributes-listing.module').then(m => m.FileAttributesListingModule),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'watermarks',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () => import('./screens/watermark/watermark.module').then(m => m.WatermarkModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'reports',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () => import('./screens/reports/reports.module').then(m => m.ReportsModule),
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.reportTemplates.read],
|
||||||
|
redirectTo: '/auth-error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-attributes',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./screens/dossier-attributes-listing/dossier-attributes-listing.module').then(m => m.DossierAttributesListingModule),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-states',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./screens/dossier-states-listing/dossier-states-listing.module').then(m => m.DossierStatesListingModule),
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'default-colors',
|
||||||
|
component: DefaultColorsScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'justifications',
|
||||||
|
component: BaseDossierTemplateScreenComponent,
|
||||||
|
loadChildren: () => import('./screens/justifications/justifications.module').then(m => m.JustificationsModule),
|
||||||
|
canActivate: [IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.legalBasis.read],
|
||||||
|
redirectTo: '/auth-error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ path: '', redirectTo: 'info', pathMatch: 'full' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const dossierTemplatesRoutes: IqserRoutes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
canActivate: [IqserAuthGuard],
|
||||||
|
loadChildren: () =>
|
||||||
|
import('./screens/dossier-templates-listing/dossier-templates-listing.module').then(m => m.DossierTemplatesListingModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `:${DOSSIER_TEMPLATE_ID}`,
|
||||||
|
children: dossierTemplateIdRoutes,
|
||||||
|
canActivate: [templateExistsWhenEnteringAdmin()],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const routes: IqserRoutes = [
|
||||||
|
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
||||||
|
{
|
||||||
|
path: 'dossier-templates',
|
||||||
|
children: dossierTemplatesRoutes,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard],
|
||||||
|
requiredRoles: ['RED_MANAGER', 'RED_ADMIN'],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.templates.read],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'users',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: UserListingScreenComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.users.read, 'RED_USER_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'dossier-permissions',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard, PermissionsGuard],
|
||||||
|
permissionsObject: 'Dossier',
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.manageAclPermissions, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loadChildren: () => import('./screens/permissions/permissions.module').then(m => m.PermissionsModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'license-info',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.license.readReport, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loadChildren: () => import('./screens/license/license.module').then(m => m.LicenseModule),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'digital-signature',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DigitalSignatureScreenComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.digitalSignature.read, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'audit',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: AuditScreenComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
||||||
|
data: {
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.searchAudit, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'general-config',
|
||||||
|
component: BaseAdminScreenComponent,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: GeneralConfigScreenComponent,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||||
|
canDeactivate: [PendingChangesGuard],
|
||||||
|
data: {
|
||||||
|
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||||
|
permissions: {
|
||||||
|
allow: [Roles.generalConfiguration.read, Roles.smtp.read, 'RED_ADMIN'],
|
||||||
|
redirectTo: '/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule],
|
||||||
|
})
|
||||||
|
export class AdminRoutingModule {}
|
||||||
130
apps/red-ui/src/app/modules/admin/admin.module.ts
Normal file
130
apps/red-ui/src/app/modules/admin/admin.module.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { AdminRoutingModule } from './admin-routing.module';
|
||||||
|
import { SharedModule } from '@shared/shared.module';
|
||||||
|
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||||
|
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||||
|
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
||||||
|
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
||||||
|
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
||||||
|
import { DossierTemplateBreadcrumbsComponent } from './shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
||||||
|
import { AddEditCloneDossierTemplateDialogComponent } from './dialogs/add-edit-dossier-template-dialog/add-edit-clone-dossier-template-dialog.component';
|
||||||
|
import { AddEntityDialogComponent } from './dialogs/add-entity-dialog/add-entity-dialog.component';
|
||||||
|
import { EditColorDialogComponent } from './dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||||
|
import { AdminDialogService } from './services/admin-dialog.service';
|
||||||
|
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
||||||
|
import { SmtpAuthDialogComponent } from './dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
||||||
|
import { AddEditUserDialogComponent } from './dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
|
||||||
|
import { UsersStatsComponent } from './components/users-stats/users-stats.component';
|
||||||
|
import { ResetPasswordComponent } from './dialogs/add-edit-user-dialog/reset-password/reset-password.component';
|
||||||
|
import { UserDetailsComponent } from './dialogs/add-edit-user-dialog/user-details/user-details.component';
|
||||||
|
import { AuditService } from './services/audit.service';
|
||||||
|
import { DigitalSignatureService } from './services/digital-signature.service';
|
||||||
|
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
||||||
|
import { RulesService } from './services/rules.service';
|
||||||
|
import { SmtpConfigService } from './services/smtp-config.service';
|
||||||
|
import { UploadDictionaryDialogComponent } from './dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
|
||||||
|
import { GeneralConfigFormComponent } from './screens/general-config/general-config-form/general-config-form.component';
|
||||||
|
import { SmtpFormComponent } from './screens/general-config/smtp-form/smtp-form.component';
|
||||||
|
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
||||||
|
import { A11yModule } from '@angular/cdk/a11y';
|
||||||
|
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||||
|
import { AdminSideNavComponent } from './shared/components/admin-side-nav/admin-side-nav.component';
|
||||||
|
import { SystemPreferencesFormComponent } from './screens/general-config/system-preferences-form/system-preferences-form.component';
|
||||||
|
import { ConfigureCertificateDialogComponent } from './dialogs/configure-digital-signature-dialog/configure-certificate-dialog.component';
|
||||||
|
import { PkcsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
||||||
|
import { KmsSignatureConfigurationComponent } from './dialogs/configure-digital-signature-dialog/form/kms-signature-configuration/kms-signature-configuration.component';
|
||||||
|
import {
|
||||||
|
ChevronButtonComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
DetailsRadioComponent,
|
||||||
|
EditableInputComponent,
|
||||||
|
EmptyStateComponent,
|
||||||
|
HasScrollbarDirective,
|
||||||
|
HumanizePipe,
|
||||||
|
IconButtonComponent,
|
||||||
|
InputWithActionComponent,
|
||||||
|
IqserAllowDirective,
|
||||||
|
IqserDenyDirective,
|
||||||
|
IqserHelpModeModule,
|
||||||
|
IqserListingModule,
|
||||||
|
IqserUploadFileModule,
|
||||||
|
RoundCheckboxComponent,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AuditInfoDialogComponent } from './dialogs/audit-info-dialog/audit-info-dialog.component';
|
||||||
|
import { DossierTemplateActionsComponent } from './shared/components/dossier-template-actions/dossier-template-actions.component';
|
||||||
|
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||||
|
import { SelectComponent } from '@shared/components/select/select.component';
|
||||||
|
import { PaginationComponent } from '@common-ui/pagination/pagination.component';
|
||||||
|
|
||||||
|
const dialogs = [
|
||||||
|
AddEditCloneDossierTemplateDialogComponent,
|
||||||
|
AddEntityDialogComponent,
|
||||||
|
EditColorDialogComponent,
|
||||||
|
SmtpAuthDialogComponent,
|
||||||
|
AddEditUserDialogComponent,
|
||||||
|
UploadDictionaryDialogComponent,
|
||||||
|
ConfigureCertificateDialogComponent,
|
||||||
|
AuditInfoDialogComponent,
|
||||||
|
];
|
||||||
|
|
||||||
|
const screens = [
|
||||||
|
AuditScreenComponent,
|
||||||
|
DefaultColorsScreenComponent,
|
||||||
|
EntitiesListingScreenComponent,
|
||||||
|
DigitalSignatureScreenComponent,
|
||||||
|
UserListingScreenComponent,
|
||||||
|
GeneralConfigScreenComponent,
|
||||||
|
];
|
||||||
|
|
||||||
|
const components = [
|
||||||
|
UsersStatsComponent,
|
||||||
|
ResetPasswordComponent,
|
||||||
|
UserDetailsComponent,
|
||||||
|
BaseAdminScreenComponent,
|
||||||
|
BaseDossierTemplateScreenComponent,
|
||||||
|
BaseEntityScreenComponent,
|
||||||
|
GeneralConfigFormComponent,
|
||||||
|
SmtpFormComponent,
|
||||||
|
SystemPreferencesFormComponent,
|
||||||
|
PkcsSignatureConfigurationComponent,
|
||||||
|
KmsSignatureConfigurationComponent,
|
||||||
|
|
||||||
|
...dialogs,
|
||||||
|
...screens,
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [...components],
|
||||||
|
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
SharedModule,
|
||||||
|
AdminRoutingModule,
|
||||||
|
A11yModule,
|
||||||
|
IqserUsersModule,
|
||||||
|
TranslateModule,
|
||||||
|
HumanizePipe,
|
||||||
|
IqserListingModule,
|
||||||
|
IqserUploadFileModule,
|
||||||
|
IqserHelpModeModule,
|
||||||
|
AdminSideNavComponent,
|
||||||
|
DossierTemplateActionsComponent,
|
||||||
|
DossierTemplateBreadcrumbsComponent,
|
||||||
|
IconButtonComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
ChevronButtonComponent,
|
||||||
|
EmptyStateComponent,
|
||||||
|
HasScrollbarDirective,
|
||||||
|
RoundCheckboxComponent,
|
||||||
|
InputWithActionComponent,
|
||||||
|
EditableInputComponent,
|
||||||
|
DetailsRadioComponent,
|
||||||
|
IqserAllowDirective,
|
||||||
|
IqserDenyDirective,
|
||||||
|
SelectComponent,
|
||||||
|
PaginationComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AdminModule {}
|
||||||
@ -1,302 +0,0 @@
|
|||||||
import { inject, provideEnvironmentInitializer } from '@angular/core';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
|
||||||
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
|
|
||||||
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
|
||||||
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
|
||||||
import { PermissionsGuard } from '@guards/permissions-guard';
|
|
||||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
|
||||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
|
||||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
|
||||||
import { CopilotService } from '@services/copilot.service';
|
|
||||||
import { RedRoleGuard } from '@users/red-role.guard';
|
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
|
||||||
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-screen.component';
|
|
||||||
import { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
|
||||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
|
||||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
|
||||||
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
|
||||||
import { EntitiesListingScreenComponent } from './screens/entities-listing/entities-listing-screen.component';
|
|
||||||
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
|
||||||
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
|
||||||
import { AdminDialogService } from './services/admin-dialog.service';
|
|
||||||
import { AuditService } from './services/audit.service';
|
|
||||||
import { DigitalSignatureService } from './services/digital-signature.service';
|
|
||||||
import { RulesService } from './services/rules.service';
|
|
||||||
import { SmtpConfigService } from './services/smtp-config.service';
|
|
||||||
|
|
||||||
const entityRoutes: IqserRoutes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
children: [{ path: '', component: EntitiesListingScreenComponent }],
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: `:${ENTITY_TYPE}`,
|
|
||||||
component: BaseEntityScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, entityExistsGuard()],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
loadChildren: () => import('./screens/entities/entities.routes'),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const dossierTemplateIdRoutes: IqserRoutes = [
|
|
||||||
{ path: '', redirectTo: 'info', pathMatch: 'full' },
|
|
||||||
{
|
|
||||||
path: 'info',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
loadComponent: () => import('./screens/info/dossier-template-info-screen/dossier-template-info-screen.component'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'entities',
|
|
||||||
children: entityRoutes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: BaseDossierTemplateScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'entity-rules',
|
|
||||||
loadComponent: () => import('./screens/rules/rules-screen/rules-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.rules.read],
|
|
||||||
redirectTo: 'info',
|
|
||||||
},
|
|
||||||
type: 'ENTITY',
|
|
||||||
},
|
|
||||||
providers: [
|
|
||||||
RulesService,
|
|
||||||
provideEnvironmentInitializer(() => {
|
|
||||||
return inject(CopilotService).connectAsync('/api/llm/llm-websocket');
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'component-rules',
|
|
||||||
loadComponent: () => import('./screens/rules/rules-screen/rules-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.rules.read],
|
|
||||||
redirectTo: 'info',
|
|
||||||
},
|
|
||||||
type: 'COMPONENT',
|
|
||||||
},
|
|
||||||
providers: [RulesService],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'component-mappings',
|
|
||||||
loadComponent: () => import('./screens/component-mappings/component-mappings-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'components',
|
|
||||||
loadComponent: () => import('./screens/component-definitions/component-definitions.component'),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'file-attributes',
|
|
||||||
loadComponent: () => import('./screens/file-attributes-listing/file-attributes-listing-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'watermarks',
|
|
||||||
loadChildren: () => import('./screens/watermark/watermark.routes'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'reports',
|
|
||||||
loadComponent: () => import('./screens/reports/reports-screen/reports-screen.component'),
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.reportTemplates.read],
|
|
||||||
redirectTo: '/auth-error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-attributes',
|
|
||||||
loadComponent: () => import('./screens/dossier-attributes-listing/dossier-attributes-listing-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-states',
|
|
||||||
loadComponent: () =>
|
|
||||||
import('./screens/dossier-states-listing/dossier-states-listing-screen/dossier-states-listing-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'default-colors',
|
|
||||||
component: DefaultColorsScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'justifications',
|
|
||||||
loadComponent: () => import('./screens/justifications/justifications-screen/justifications-screen.component'),
|
|
||||||
canActivate: [IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.legalBasis.read],
|
|
||||||
redirectTo: '/auth-error',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const dossierTemplatesRoutes: IqserRoutes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
loadComponent: () =>
|
|
||||||
import(
|
|
||||||
'./screens/dossier-templates-listing/dossier-templates-listing-screen/dossier-templates-listing-screen.component'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
canActivate: [IqserAuthGuard],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: `:${DOSSIER_TEMPLATE_ID}`,
|
|
||||||
children: dossierTemplateIdRoutes,
|
|
||||||
canActivate: [templateExistsWhenEnteringAdmin()],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default [
|
|
||||||
{ path: '', redirectTo: 'dossier-templates', pathMatch: 'full' },
|
|
||||||
{
|
|
||||||
path: 'dossier-templates',
|
|
||||||
children: dossierTemplatesRoutes,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, DossierTemplatesGuard],
|
|
||||||
requiredRoles: ['RED_MANAGER', 'RED_ADMIN'],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.templates.read],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: BaseAdminScreenComponent,
|
|
||||||
providers: [AdminDialogService],
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'users',
|
|
||||||
component: UserListingScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.users.read, 'RED_USER_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'dossier-permissions',
|
|
||||||
loadComponent: () => import('./screens/permissions/permissions-screen/permissions-screen.component'),
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard, PermissionsGuard],
|
|
||||||
permissionsObject: 'Dossier',
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.manageAclPermissions, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'license-info',
|
|
||||||
loadChildren: () => import('./screens/license/license.routes'),
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.license.readReport, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'digital-signature',
|
|
||||||
component: DigitalSignatureScreenComponent,
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.digitalSignature.read, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
providers: [DigitalSignatureService],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'audit',
|
|
||||||
component: AuditScreenComponent,
|
|
||||||
canActivate: [IqserAuthGuard, IqserPermissionsGuard, RedRoleGuard],
|
|
||||||
data: {
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.searchAudit, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
providers: [AuditService],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'general-config',
|
|
||||||
component: GeneralConfigScreenComponent,
|
|
||||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
|
||||||
canDeactivate: [PendingChangesGuard],
|
|
||||||
data: {
|
|
||||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
|
||||||
permissions: {
|
|
||||||
allow: [Roles.generalConfiguration.read, Roles.smtp.read, 'RED_ADMIN'],
|
|
||||||
redirectTo: '/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
providers: [SmtpConfigService],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
] satisfies IqserRoutes;
|
|
||||||
@ -1,10 +1,7 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
|
||||||
import { RouterOutlet } from '@angular/router';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-admin-screen.component.html',
|
templateUrl: './base-admin-screen.component.html',
|
||||||
styleUrls: ['./base-admin-screen.component.scss'],
|
styleUrls: ['./base-admin-screen.component.scss'],
|
||||||
imports: [AdminSideNavComponent, RouterOutlet],
|
|
||||||
})
|
})
|
||||||
export class BaseAdminScreenComponent {}
|
export class BaseAdminScreenComponent {}
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
<!--TODO: Use this for all dossier template screens -->
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|||||||
@ -1,22 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { DossierTemplateBreadcrumbsComponent } from '../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
|
||||||
import { DossierTemplateActionsComponent } from '../shared/components/dossier-template-actions/dossier-template-actions.component';
|
|
||||||
import { CircleButtonComponent } from '@iqser/common-ui';
|
|
||||||
import { RouterLink, RouterOutlet } from '@angular/router';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-dossier-template-screen.component.html',
|
templateUrl: './base-dossier-template-screen.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
|
||||||
DossierTemplateBreadcrumbsComponent,
|
|
||||||
DossierTemplateActionsComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
RouterLink,
|
|
||||||
TranslateModule,
|
|
||||||
AdminSideNavComponent,
|
|
||||||
RouterOutlet,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class BaseDossierTemplateScreenComponent {}
|
export class BaseDossierTemplateScreenComponent {}
|
||||||
|
|||||||
@ -1,33 +1,19 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
import { DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||||
import { Router, RouterLink, RouterOutlet } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
import { AdminDialogService } from '../services/admin-dialog.service';
|
import { AdminDialogService } from '../services/admin-dialog.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { CircleButtonComponent, LoadingService } from '@iqser/common-ui';
|
import { LoadingService } from '@iqser/common-ui';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||||
import { DossierTemplateBreadcrumbsComponent } from '../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { AdminSideNavComponent } from '../shared/components/admin-side-nav/admin-side-nav.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './base-entity-screen.component.html',
|
templateUrl: './base-entity-screen.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
|
||||||
DossierTemplateBreadcrumbsComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
NgIf,
|
|
||||||
AsyncPipe,
|
|
||||||
TranslateModule,
|
|
||||||
RouterLink,
|
|
||||||
AdminSideNavComponent,
|
|
||||||
RouterOutlet,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class BaseEntityScreenComponent implements OnInit {
|
export class BaseEntityScreenComponent implements OnInit {
|
||||||
readonly disabledItems$: Observable<string[]>;
|
readonly disabledItems$: Observable<string[]>;
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<div class="mt-44">
|
<div class="mt-44">
|
||||||
<redaction-donut-chart
|
<redaction-donut-chart
|
||||||
[config]="chartConfig()"
|
[config]="chartConfig"
|
||||||
[radius]="63"
|
[radius]="63"
|
||||||
[strokeWidth]="15"
|
[strokeWidth]="15"
|
||||||
[subtitles]="['user-stats.chart.users' | translate]"
|
[subtitles]="['user-stats.chart.users' | translate]"
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import { Component, EventEmitter, input, Input, output, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { DonutChartConfig } from '@red/domain';
|
import { DonutChartConfig } from '@red/domain';
|
||||||
import { CircleButtonComponent } from '@iqser/common-ui';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-users-stats',
|
selector: 'redaction-users-stats',
|
||||||
templateUrl: './users-stats.component.html',
|
templateUrl: './users-stats.component.html',
|
||||||
styleUrls: ['./users-stats.component.scss'],
|
styleUrls: ['./users-stats.component.scss'],
|
||||||
imports: [CircleButtonComponent, TranslateModule, DonutChartComponent],
|
|
||||||
})
|
})
|
||||||
export class UsersStatsComponent {
|
export class UsersStatsComponent {
|
||||||
readonly chartConfig = input.required<DonutChartConfig[]>();
|
@Output() toggleCollapse = new EventEmitter();
|
||||||
readonly toggleCollapse = output();
|
@Input() chartConfig: DonutChartConfig[];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
<section class="dialog">
|
|
||||||
<div [innerHTML]="'add-clone-dossier-template.title' | translate: translateParams" class="dialog-header heading-l"></div>
|
|
||||||
|
|
||||||
<form [formGroup]="form">
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="iqser-input-group required w-300">
|
|
||||||
<label [translate]="'add-edit-clone-dossier-template.form.name'"></label>
|
|
||||||
<input
|
|
||||||
[placeholder]="'add-edit-clone-dossier-template.form.name-placeholder' | translate"
|
|
||||||
formControlName="name"
|
|
||||||
name="name"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group w-400">
|
|
||||||
<label [translate]="'add-edit-clone-dossier-template.form.description'"></label>
|
|
||||||
<textarea
|
|
||||||
[placeholder]="'add-edit-clone-dossier-template.form.description-placeholder' | translate"
|
|
||||||
formControlName="description"
|
|
||||||
name="description"
|
|
||||||
rows="4"
|
|
||||||
type="text"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="save()"
|
|
||||||
[buttonId]="'saveButton'"
|
|
||||||
[disabled]="disabled"
|
|
||||||
[label]="'add-clone-dossier-template.save' | translate: translateParams"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
></iqser-icon-button>
|
|
||||||
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="save({ nextAction: true })"
|
|
||||||
[buttonId]="'saveButton'"
|
|
||||||
[disabled]="disabled"
|
|
||||||
[label]="'add-clone-dossier-template.save-and-edit' | translate"
|
|
||||||
[type]="iconButtonTypes.dark"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
|
||||||
</section>
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
import { HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { Component, Inject } from '@angular/core';
|
|
||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { BaseDialogComponent, CircleButtonComponent, getConfig, IconButtonComponent, SaveOptions } from '@iqser/common-ui';
|
|
||||||
import { DossierTemplate } from '@red/domain';
|
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
export interface CloneTemplateData {
|
|
||||||
dossierTemplateId?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './add-clone-dossier-template-dialog.component.html',
|
|
||||||
styleUrls: ['./add-clone-dossier-template-dialog.component.scss'],
|
|
||||||
imports: [TranslateModule, ReactiveFormsModule, IconButtonComponent, CircleButtonComponent],
|
|
||||||
})
|
|
||||||
export class AddCloneDossierTemplateDialogComponent extends BaseDialogComponent {
|
|
||||||
readonly dossierTemplate?: DossierTemplate;
|
|
||||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
readonly translateParams: { type: string; dossierTemplateName?: string };
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
|
||||||
private readonly _router: Router,
|
|
||||||
protected readonly _dialogRef: MatDialogRef<AddCloneDossierTemplateDialogComponent>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) readonly data: CloneTemplateData,
|
|
||||||
) {
|
|
||||||
super(_dialogRef);
|
|
||||||
this.dossierTemplate = this._dossierTemplatesService.find(this.data.dossierTemplateId);
|
|
||||||
|
|
||||||
this.translateParams = {
|
|
||||||
type: this.dossierTemplate ? 'clone' : 'create',
|
|
||||||
dossierTemplateName: this.dossierTemplate?.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
override get disabled(): boolean {
|
|
||||||
// Ignore 'changed' value, doesn't make sense in this context
|
|
||||||
return !this.valid || this._hasErrors();
|
|
||||||
}
|
|
||||||
|
|
||||||
async save(options?: SaveOptions): Promise<void> {
|
|
||||||
let dossierTemplate: DossierTemplate;
|
|
||||||
this._loadingService.start();
|
|
||||||
const body = {
|
|
||||||
...this.dossierTemplate,
|
|
||||||
...this.form.getRawValue(),
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
if (this.dossierTemplate) {
|
|
||||||
dossierTemplate = await this._dossierTemplatesService.clone(this.dossierTemplate.id, body);
|
|
||||||
} else {
|
|
||||||
dossierTemplate = await this._dossierTemplatesService.createOrUpdate(body);
|
|
||||||
}
|
|
||||||
if (options?.nextAction) {
|
|
||||||
await this._router.navigate([dossierTemplate.routerLink]);
|
|
||||||
}
|
|
||||||
this._dialogRef.close(true);
|
|
||||||
} catch (error) {
|
|
||||||
if (error.status === HttpStatusCode.Conflict) {
|
|
||||||
this._toaster.error(_('add-edit-clone-dossier-template.error.conflict'), { error });
|
|
||||||
} else {
|
|
||||||
this._toaster.rawError(error.error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm() {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
name: [this.dossierTemplate ? this.#getCloneName(this.dossierTemplate) : undefined, Validators.required],
|
|
||||||
description: [this.dossierTemplate?.description],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#getCloneName(initialTemplate: DossierTemplate): string {
|
|
||||||
const templateName = initialTemplate.name.trim();
|
|
||||||
let nameOfClonedTemplate: string = templateName.split('Copy of ').filter(n => n)[0];
|
|
||||||
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
|
||||||
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
|
||||||
|
|
||||||
let clonesCount = 0;
|
|
||||||
for (const name of allTemplatesNames) {
|
|
||||||
const splitName = name.split(nameOfClonedTemplate);
|
|
||||||
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
|
||||||
if (splitName[0] === 'Copy of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
|
||||||
clonesCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clonesCount >= 1) {
|
|
||||||
return `Copy of ${nameOfClonedTemplate} (${clonesCount})`;
|
|
||||||
}
|
|
||||||
return `Copy of ${nameOfClonedTemplate}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,8 @@
|
|||||||
<div class="content-container" iqserHasScrollbar>
|
<section class="dialog">
|
||||||
<form [formGroup]="form" class="dialog">
|
<div [innerHTML]="'add-edit-clone-dossier-template.title' | translate: translateParams" class="dialog-header heading-l"></div>
|
||||||
|
|
||||||
|
<form [formGroup]="form">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<redaction-dossier-template-details [dossierTemplateId]="dossierTemplateId"></redaction-dossier-template-details>
|
|
||||||
|
|
||||||
<div class="heading-md mt-24 mb-8" translate="dossier-template-info-screen.title"></div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group required w-300">
|
<div class="iqser-input-group required w-300">
|
||||||
<label [translate]="'add-edit-clone-dossier-template.form.name'"></label>
|
<label [translate]="'add-edit-clone-dossier-template.form.name'"></label>
|
||||||
<input
|
<input
|
||||||
@ -15,7 +13,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group w-full">
|
<div class="iqser-input-group w-400">
|
||||||
<label [translate]="'add-edit-clone-dossier-template.form.description'"></label>
|
<label [translate]="'add-edit-clone-dossier-template.form.description'"></label>
|
||||||
<textarea
|
<textarea
|
||||||
[placeholder]="'add-edit-clone-dossier-template.form.description-placeholder' | translate"
|
[placeholder]="'add-edit-clone-dossier-template.form.description-placeholder' | translate"
|
||||||
@ -26,14 +24,20 @@
|
|||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="validity mt-12">
|
<div class="validity">
|
||||||
<div>
|
<div>
|
||||||
<mat-checkbox (change)="toggleHasValid('from')" [checked]="hasValidFrom()" color="primary">
|
<mat-checkbox (change)="toggleHasValid('from')" [checked]="hasValidFrom" class="filter-menu-checkbox" color="primary">
|
||||||
{{ 'add-edit-clone-dossier-template.form.valid-from' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.valid-from' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
|
|
||||||
|
<mat-checkbox (change)="toggleHasValid('to')" [checked]="hasValidTo" class="filter-menu-checkbox" color="primary">
|
||||||
|
{{ 'add-edit-clone-dossier-template.form.valid-to' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
<div class="iqser-input-group datepicker-wrapper">
|
<div class="iqser-input-group datepicker-wrapper">
|
||||||
@if (hasValidFrom()) {
|
<ng-container *ngIf="hasValidFrom">
|
||||||
<input
|
<input
|
||||||
(dateChange)="applyValidityIntervalConstraints()"
|
(dateChange)="applyValidityIntervalConstraints()"
|
||||||
[matDatepicker]="fromPicker"
|
[matDatepicker]="fromPicker"
|
||||||
@ -44,16 +48,11 @@
|
|||||||
<mat-icon matDatepickerToggleIcon svgIcon="iqser:calendar"></mat-icon>
|
<mat-icon matDatepickerToggleIcon svgIcon="iqser:calendar"></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
<mat-datepicker #fromPicker></mat-datepicker>
|
<mat-datepicker #fromPicker></mat-datepicker>
|
||||||
}
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<mat-checkbox (change)="toggleHasValid('to')" [checked]="hasValidTo()" color="primary">
|
|
||||||
{{ 'add-edit-clone-dossier-template.form.valid-to' | translate }}
|
|
||||||
</mat-checkbox>
|
|
||||||
<div class="iqser-input-group datepicker-wrapper">
|
<div class="iqser-input-group datepicker-wrapper">
|
||||||
@if (hasValidTo()) {
|
<ng-container *ngIf="hasValidTo">
|
||||||
<input
|
<input
|
||||||
(dateChange)="applyValidityIntervalConstraints()"
|
(dateChange)="applyValidityIntervalConstraints()"
|
||||||
[matDatepicker]="toPicker"
|
[matDatepicker]="toPicker"
|
||||||
@ -64,43 +63,45 @@
|
|||||||
<mat-icon matDatepickerToggleIcon svgIcon="iqser:calendar"></mat-icon>
|
<mat-icon matDatepickerToggleIcon svgIcon="iqser:calendar"></mat-icon>
|
||||||
</mat-datepicker-toggle>
|
</mat-datepicker-toggle>
|
||||||
<mat-datepicker #toPicker></mat-datepicker>
|
<mat-datepicker #toPicker></mat-datepicker>
|
||||||
}
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!isDocumine) {
|
<div *ngIf="!isDocumine">
|
||||||
<div class="mt-24">
|
<p class="heading download-includes">
|
||||||
<div class="heading">
|
{{ 'add-edit-clone-dossier-template.form.apply-updates-default.heading' | translate }}
|
||||||
{{ 'add-edit-clone-dossier-template.form.apply-updates-default.heading' | translate }}
|
</p>
|
||||||
</div>
|
<div class="iqser-input-group">
|
||||||
<div class="iqser-input-group">
|
<mat-checkbox color="primary" formControlName="applyDictionaryUpdatesToAllDossiersByDefault">
|
||||||
<mat-checkbox color="primary" formControlName="applyDictionaryUpdatesToAllDossiersByDefault">
|
{{ 'add-edit-clone-dossier-template.form.apply-updates-default.description' | translate }}
|
||||||
{{ 'add-edit-clone-dossier-template.form.apply-updates-default.description' | translate }}
|
</mat-checkbox>
|
||||||
</mat-checkbox>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div *ngIf="!isDocumine" class="flex">
|
||||||
|
<div class="half-flex-basis">
|
||||||
|
<p class="heading download-includes">{{ 'download-includes' | translate }}</p>
|
||||||
|
|
||||||
|
<div class="flex">
|
||||||
|
<redaction-select
|
||||||
|
[label]="
|
||||||
|
'download-type.label'
|
||||||
|
| translate
|
||||||
|
: {
|
||||||
|
length: form.controls['downloadFileTypes'].value.length
|
||||||
|
}
|
||||||
|
"
|
||||||
|
[options]="downloadTypes"
|
||||||
|
formControlName="downloadFileTypes"
|
||||||
|
></redaction-select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-24">
|
<div class="half-flex-basis w-full pl-75">
|
||||||
<div class="heading mb-14">{{ 'download-includes' | translate }}</div>
|
<p class="heading download-includes">
|
||||||
|
|
||||||
<redaction-select
|
|
||||||
[label]="
|
|
||||||
'download-type.label'
|
|
||||||
| translate
|
|
||||||
: {
|
|
||||||
length: form.controls['downloadFileTypes'].value.length,
|
|
||||||
}
|
|
||||||
"
|
|
||||||
[options]="downloadTypes"
|
|
||||||
formControlName="downloadFileTypes"
|
|
||||||
></redaction-select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-24">
|
|
||||||
<div class="heading">
|
|
||||||
{{ 'add-edit-clone-dossier-template.form.upload-settings.heading' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.upload-settings.heading' | translate }}
|
||||||
</div>
|
</p>
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="ocrByDefault">
|
<mat-checkbox color="primary" formControlName="ocrByDefault">
|
||||||
{{ 'add-edit-clone-dossier-template.form.upload-settings.ocr-by-default' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.upload-settings.ocr-by-default' | translate }}
|
||||||
@ -112,41 +113,50 @@
|
|||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-24 hidden-elements">
|
<div *ngIf="!isDocumine">
|
||||||
<div class="heading">{{ 'add-edit-clone-dossier-template.form.hidden-text.heading' | translate }}</div>
|
<p class="heading download-includes">{{ 'add-edit-clone-dossier-template.form.hidden-text.heading' | translate }}</p>
|
||||||
|
<div class="hidden-elements">
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="keepHiddenText">
|
<mat-checkbox color="primary" formControlName="keepHiddenText">
|
||||||
{{ 'add-edit-clone-dossier-template.form.hidden-text.title' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.hidden-text.title' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
<div class="info">{{ 'add-edit-clone-dossier-template.form.hidden-text.description' | translate }}</div>
|
<div class="info mt-4">{{ 'add-edit-clone-dossier-template.form.hidden-text.description' | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="keepImageMetadata">
|
<mat-checkbox color="primary" formControlName="keepImageMetadata">
|
||||||
{{ 'add-edit-clone-dossier-template.form.image-metadata.title' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.image-metadata.title' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
<div class="info">{{ 'add-edit-clone-dossier-template.form.image-metadata.description' | translate }}</div>
|
<div class="info mt-4">{{ 'add-edit-clone-dossier-template.form.image-metadata.description' | translate }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="iqser-input-group">
|
<div class="iqser-input-group">
|
||||||
<mat-checkbox color="primary" formControlName="keepOverlappingObjects">
|
<mat-checkbox color="primary" formControlName="keepOverlappingObjects">
|
||||||
{{ 'add-edit-clone-dossier-template.form.overlapping-elements.title' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.overlapping-elements.title' | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
<div class="info">
|
<div class="info mt-4">
|
||||||
{{ 'add-edit-clone-dossier-template.form.overlapping-elements.description' | translate }}
|
{{ 'add-edit-clone-dossier-template.form.overlapping-elements.description' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[buttonId]="'saveButton'"
|
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
[label]="'add-edit-clone-dossier-template.save' | translate"
|
[label]="'add-edit-clone-dossier-template.save' | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
|
[buttonId]="'saveButton'"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
|
<iqser-help-button
|
||||||
|
*ngIf="!isDocumine && !!dossierTemplate"
|
||||||
|
[helpButtonKey]="'edit_clone_delete_dossier_templates'"
|
||||||
|
></iqser-help-button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
|
||||||
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
|
</section>
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
.validity {
|
||||||
|
width: 230px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
margin-right: 16px;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iqser-input-group {
|
||||||
|
min-height: 42px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redaction-select {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-includes {
|
||||||
|
margin: 16px 0 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden-elements {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
|
||||||
|
.iqser-input-group {
|
||||||
|
margin-top: 0;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
mat-checkbox {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.half-flex-basis {
|
||||||
|
flex-basis: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pl-75 {
|
||||||
|
padding: 0 0 0 75px;
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { Component, Inject } from '@angular/core';
|
||||||
|
import { AbstractControl, Validators } from '@angular/forms';
|
||||||
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { BaseDialogComponent, getConfig } from '@iqser/common-ui';
|
||||||
|
import { DossierTemplate, IDossierTemplate } from '@red/domain';
|
||||||
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
|
import { downloadTypesTranslations } from '@translations/download-types-translations';
|
||||||
|
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||||
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
|
interface EditCloneTemplateData {
|
||||||
|
dossierTemplateId: string;
|
||||||
|
clone?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadTypes = ['ORIGINAL', 'PREVIEW', 'DELTA_PREVIEW', 'REDACTED'].map(type => ({
|
||||||
|
key: type,
|
||||||
|
label: downloadTypesTranslations[type],
|
||||||
|
}));
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: './add-edit-clone-dossier-template-dialog.component.html',
|
||||||
|
styleUrls: ['./add-edit-clone-dossier-template-dialog.component.scss'],
|
||||||
|
})
|
||||||
|
export class AddEditCloneDossierTemplateDialogComponent extends BaseDialogComponent {
|
||||||
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
hasValidFrom: boolean;
|
||||||
|
hasValidTo: boolean;
|
||||||
|
readonly downloadTypes = downloadTypes;
|
||||||
|
readonly dossierTemplate: DossierTemplate;
|
||||||
|
private _previousValidFrom: Dayjs;
|
||||||
|
private _previousValidTo: Dayjs;
|
||||||
|
private _lastValidFrom: Dayjs;
|
||||||
|
private _lastValidTo: Dayjs;
|
||||||
|
|
||||||
|
get disabled(): boolean {
|
||||||
|
if (!this.data?.clone) {
|
||||||
|
return super.disabled;
|
||||||
|
}
|
||||||
|
return !this.valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get translateParams() {
|
||||||
|
return {
|
||||||
|
type: this.dossierTemplate ? (this.data.clone ? 'clone' : 'edit') : 'create',
|
||||||
|
name: this.dossierTemplate?.name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
|
protected readonly _dialogRef: MatDialogRef<AddEditCloneDossierTemplateDialogComponent>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) readonly data: EditCloneTemplateData,
|
||||||
|
) {
|
||||||
|
super(_dialogRef, !!data && !data.clone);
|
||||||
|
this.dossierTemplate = this._dossierTemplatesService.find(this.data?.dossierTemplateId);
|
||||||
|
this.form = this.#getForm();
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
|
this.hasValidFrom = !!this.dossierTemplate?.validFrom;
|
||||||
|
this.hasValidTo = !!this.dossierTemplate?.validTo;
|
||||||
|
|
||||||
|
this._previousValidFrom = this._lastValidFrom = this.form.get('validFrom').value;
|
||||||
|
this._previousValidTo = this._lastValidTo = this.form.get('validTo').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleHasValid(extremity: string) {
|
||||||
|
if (extremity === 'from') {
|
||||||
|
this.hasValidFrom = !this.hasValidFrom;
|
||||||
|
this.form.controls['validFrom'].setValue(this.hasValidFrom ? this._lastValidFrom : null);
|
||||||
|
} else {
|
||||||
|
this.hasValidTo = !this.hasValidTo;
|
||||||
|
this.form.controls['validTo'].setValue(this.hasValidTo ? this._lastValidTo : null);
|
||||||
|
}
|
||||||
|
this.applyValidityIntervalConstraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
async save() {
|
||||||
|
this._loadingService.start();
|
||||||
|
const dossierTemplate = {
|
||||||
|
dossierTemplateId: this.dossierTemplate?.dossierTemplateId,
|
||||||
|
...this.form.getRawValue(),
|
||||||
|
validFrom: this.hasValidFrom ? this.form.get('validFrom').value : null,
|
||||||
|
validTo: this.hasValidTo ? this.form.get('validTo').value : null,
|
||||||
|
} as IDossierTemplate;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.data?.clone) {
|
||||||
|
await this._dossierTemplatesService.clone(this.dossierTemplate.id, dossierTemplate);
|
||||||
|
} else {
|
||||||
|
await this._dossierTemplatesService.createOrUpdate(dossierTemplate);
|
||||||
|
}
|
||||||
|
this._dialogRef.close(true);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
|
this._toaster.error(_('add-edit-clone-dossier-template.error.conflict'), { error });
|
||||||
|
} else {
|
||||||
|
this._toaster.rawError(error.error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
applyValidityIntervalConstraints(): void {
|
||||||
|
const formValue = this.form.value;
|
||||||
|
applyIntervalConstraints(formValue, this._previousValidFrom, this._previousValidTo, this.form, 'validFrom', 'validTo');
|
||||||
|
|
||||||
|
this._previousValidFrom = this.form.get('validFrom').value;
|
||||||
|
this._previousValidTo = this.form.get('validTo').value;
|
||||||
|
this._lastValidFrom = this._previousValidFrom || this._lastValidFrom;
|
||||||
|
this._lastValidTo = this._previousValidTo || this._lastValidTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getForm() {
|
||||||
|
return this._formBuilder.group({
|
||||||
|
name: [this.#getCloneName(), Validators.required],
|
||||||
|
description: [this.dossierTemplate?.description],
|
||||||
|
validFrom: [
|
||||||
|
this.dossierTemplate?.validFrom ? dayjs(this.dossierTemplate?.validFrom) : null,
|
||||||
|
this.#requiredIfValidator(() => this.hasValidFrom),
|
||||||
|
],
|
||||||
|
validTo: [
|
||||||
|
this.dossierTemplate?.validTo ? dayjs(this.dossierTemplate?.validTo) : null,
|
||||||
|
this.#requiredIfValidator(() => this.hasValidTo),
|
||||||
|
],
|
||||||
|
applyDictionaryUpdatesToAllDossiersByDefault: [this.dossierTemplate?.applyDictionaryUpdatesToAllDossiersByDefault],
|
||||||
|
ocrByDefault: [this.dossierTemplate?.ocrByDefault],
|
||||||
|
removeWatermark: [this.dossierTemplate?.removeWatermark],
|
||||||
|
downloadFileTypes: [this.dossierTemplate?.downloadFileTypes || ['PREVIEW', 'REDACTED']],
|
||||||
|
keepHiddenText: [this.dossierTemplate?.keepHiddenText],
|
||||||
|
keepImageMetadata: [this.dossierTemplate?.keepImageMetadata],
|
||||||
|
keepOverlappingObjects: [this.dossierTemplate?.keepOverlappingObjects],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#getCloneName(): string {
|
||||||
|
if (!this.data?.clone) {
|
||||||
|
return this.dossierTemplate?.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateName = this.dossierTemplate.name.trim();
|
||||||
|
let nameOfClonedTemplate: string = templateName.split('Copy of ').filter(n => n)[0];
|
||||||
|
nameOfClonedTemplate = nameOfClonedTemplate.split(/\(\s*\d+\s*\)$/)[0].trim();
|
||||||
|
const allTemplatesNames = this._dossierTemplatesService.all.map(t => t.name);
|
||||||
|
|
||||||
|
let clonesCount = 0;
|
||||||
|
for (const name of allTemplatesNames) {
|
||||||
|
const splitName = name.split(nameOfClonedTemplate);
|
||||||
|
const suffixRegExp = new RegExp(/^\(\s*\d+\s*\)$/);
|
||||||
|
if (splitName[0] === 'Copy of ' && (splitName[1].trim().match(suffixRegExp) || splitName[1] === '')) {
|
||||||
|
clonesCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clonesCount >= 1) {
|
||||||
|
return `Copy of ${nameOfClonedTemplate} (${clonesCount})`;
|
||||||
|
}
|
||||||
|
return `Copy of ${nameOfClonedTemplate}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
#requiredIfValidator(predicate) {
|
||||||
|
return (formControl: AbstractControl) => {
|
||||||
|
if (!formControl.parent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (predicate()) {
|
||||||
|
return Validators.required(formControl);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,23 +1,18 @@
|
|||||||
import { Component, Inject, ViewChild } from '@angular/core';
|
import { Component, Inject, ViewChild } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { BaseDialogComponent, CircleButtonComponent } from '@iqser/common-ui';
|
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { ResetPasswordComponent } from './reset-password/reset-password.component';
|
|
||||||
import { UserDetailsComponent } from './user-details/user-details.component';
|
import { UserDetailsComponent } from './user-details/user-details.component';
|
||||||
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-add-edit-user-dialog',
|
selector: 'redaction-add-edit-user-dialog',
|
||||||
templateUrl: './add-edit-user-dialog.component.html',
|
templateUrl: './add-edit-user-dialog.component.html',
|
||||||
imports: [UserDetailsComponent, ResetPasswordComponent, CircleButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
export class AddEditUserDialogComponent extends BaseDialogComponent {
|
||||||
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
|
||||||
resettingPassword = false;
|
resettingPassword = false;
|
||||||
|
@ViewChild(UserDetailsComponent) private readonly _userDetailsComponent: UserDetailsComponent;
|
||||||
|
|
||||||
constructor(
|
constructor(protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>, @Inject(MAT_DIALOG_DATA) readonly user: User) {
|
||||||
protected readonly _dialogRef: MatDialogRef<AddEditUserDialogComponent>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) readonly user: User,
|
|
||||||
) {
|
|
||||||
super(_dialogRef, !!user);
|
super(_dialogRef, !!user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,13 @@
|
|||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { IconButtonComponent, IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NamePipe } from '@common-ui/users/name.pipe';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-reset-password',
|
selector: 'redaction-reset-password',
|
||||||
templateUrl: './reset-password.component.html',
|
templateUrl: './reset-password.component.html',
|
||||||
imports: [TranslateModule, NamePipe, ReactiveFormsModule, IconButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class ResetPasswordComponent {
|
export class ResetPasswordComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<div
|
<div
|
||||||
[translateParams]="{
|
[translateParams]="{
|
||||||
type: !!user() ? 'edit' : 'create',
|
type: user ? 'edit' : 'create'
|
||||||
}"
|
}"
|
||||||
[translate]="'add-edit-user.title'"
|
[translate]="'add-edit-user.title'"
|
||||||
class="dialog-header heading-l"
|
class="dialog-header heading-l"
|
||||||
@ -32,22 +32,14 @@
|
|||||||
[formControlName]="role"
|
[formControlName]="role"
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{{ translations[role] | translate: { count: 1 } }}
|
{{ translations[role] | translate }}
|
||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if (!user()) {
|
|
||||||
<div class="iqser-input-group">
|
|
||||||
<label [translate]="'add-edit-user.form.account-setup'"></label>
|
|
||||||
<mat-checkbox formControlName="sendSetPasswordMail">{{ 'add-edit-user.form.send-email' | translate }}</mat-checkbox>
|
|
||||||
<span [translate]="'add-edit-user.form.send-email-explanation'" class="hint"></span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
(click)="toggleResetPassword.emit()"
|
(click)="toggleResetPassword.emit()"
|
||||||
*ngIf="!!user()"
|
*ngIf="!!user"
|
||||||
[translate]="'add-edit-user.form.reset-password'"
|
[translate]="'add-edit-user.form.reset-password'"
|
||||||
class="mt-24 fit-content link-action"
|
class="mt-24 fit-content link-action"
|
||||||
></div>
|
></div>
|
||||||
@ -56,14 +48,14 @@
|
|||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
[disabled]="form.invalid || !changed"
|
[disabled]="form.invalid || !changed"
|
||||||
[label]="(user() ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate"
|
[label]="(user ? 'add-edit-user.actions.save-changes' : 'add-edit-user.actions.save') | translate"
|
||||||
[submit]="true"
|
[submit]="true"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="delete()"
|
(action)="delete()"
|
||||||
*ngIf="user() && !disabledDelete(user())"
|
*ngIf="user && !disabledDelete(user)"
|
||||||
[label]="'add-edit-user.actions.delete' | translate"
|
[label]="'add-edit-user.actions.delete' | translate"
|
||||||
[type]="iconButtonTypes.dark"
|
[type]="iconButtonTypes.dark"
|
||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
|
|||||||
@ -5,7 +5,3 @@
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hint {
|
|
||||||
margin-left: 23px;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,31 +1,29 @@
|
|||||||
import { Component, input, OnInit, output } from '@angular/core';
|
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../../services/admin-dialog.service';
|
||||||
import { BaseFormComponent, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { rolesTranslations } from '@translations/roles-translations';
|
import { rolesTranslations } from '@translations/roles-translations';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { User } from '@red/domain';
|
import { User } from '@red/domain';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-details',
|
selector: 'redaction-user-details',
|
||||||
templateUrl: './user-details.component.html',
|
templateUrl: './user-details.component.html',
|
||||||
styleUrls: ['./user-details.component.scss'],
|
styleUrls: ['./user-details.component.scss'],
|
||||||
imports: [TranslateModule, ReactiveFormsModule, MatCheckbox, NgForOf, IconButtonComponent, NgIf],
|
|
||||||
})
|
})
|
||||||
export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
export class UserDetailsComponent extends BaseFormComponent implements OnChanges {
|
||||||
user = input<User>();
|
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
||||||
readonly toggleResetPassword = output();
|
private readonly _ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
||||||
readonly closeDialog = output<boolean>();
|
@Input() user: User;
|
||||||
readonly cancel = output();
|
@Output() readonly toggleResetPassword = new EventEmitter();
|
||||||
|
@Output() readonly closeDialog = new EventEmitter();
|
||||||
|
@Output() readonly cancel = new EventEmitter();
|
||||||
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
readonly ROLES = ['RED_USER', 'RED_MANAGER', 'RED_USER_ADMIN', 'RED_ADMIN'];
|
||||||
readonly translations = rolesTranslations;
|
readonly translations = rolesTranslations;
|
||||||
/** e.g. a RED_ADMIN is automatically a RED_USER_ADMIN => can't disable RED_USER_ADMIN as long as RED_ADMIN is checked */
|
|
||||||
readonly #ROLE_REQUIREMENTS = { RED_MANAGER: 'RED_USER', RED_ADMIN: 'RED_USER_ADMIN' };
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _formBuilder: UntypedFormBuilder,
|
private readonly _formBuilder: UntypedFormBuilder,
|
||||||
@ -46,17 +44,13 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
get sendSetPasswordMail() {
|
private get _rolesControls(): any {
|
||||||
return !this.form.controls.sendSetPasswordMail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get #rolesControls() {
|
|
||||||
return this.ROLES.reduce(
|
return this.ROLES.reduce(
|
||||||
(prev, role) => ({
|
(prev, role) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[role]: [
|
[role]: [
|
||||||
{
|
{
|
||||||
value: this.user() && this.user().has(role),
|
value: this.user && this.user.has(role),
|
||||||
disabled: this.shouldBeDisabled(role),
|
disabled: this.shouldBeDisabled(role),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -65,20 +59,20 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnChanges() {
|
||||||
this.form = this.#getForm();
|
this.form = this._getForm();
|
||||||
this.initialFormValue = this.form.getRawValue();
|
this.initialFormValue = this.form.getRawValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldBeDisabled(role: string): boolean {
|
shouldBeDisabled(role: string): boolean {
|
||||||
const isCurrentAdminUser = this.user() && this.user().isAdmin && this.user().id === this._userService.currentUser.id;
|
const isCurrentAdminUser = this.user && this.user.isAdmin && this.user.id === this._userService.currentUser.id;
|
||||||
return (
|
return (
|
||||||
// RED_ADMIN can't remove own RED_ADMIN role
|
// RED_ADMIN can't remove own RED_ADMIN role
|
||||||
(role === 'RED_ADMIN' && isCurrentAdminUser) ||
|
(role === 'RED_ADMIN' && isCurrentAdminUser) ||
|
||||||
// only RED_ADMINs can edit RED_ADMIN roles
|
// only RED_ADMINs can edit RED_ADMIN roles
|
||||||
(role === 'RED_ADMIN' && !this._userService.currentUser.isAdmin) ||
|
(role === 'RED_ADMIN' && !this._userService.currentUser.isAdmin) ||
|
||||||
Object.keys(this.#ROLE_REQUIREMENTS).reduce(
|
Object.keys(this._ROLE_REQUIREMENTS).reduce(
|
||||||
(value, key) => value || (role === this.#ROLE_REQUIREMENTS[key] && this.user()?.roles.includes(key)),
|
(value, key) => value || (role === this._ROLE_REQUIREMENTS[key] && this.user?.roles.includes(key)),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -86,38 +80,38 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const userData: IProfileUpdateRequest = {
|
const userData: IProfileUpdateRequest = { ...this.form.getRawValue(), roles: this.activeRoles };
|
||||||
...this.form.getRawValue(),
|
|
||||||
roles: this.activeRoles,
|
|
||||||
sendSetPasswordMail: this.sendSetPasswordMail,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this.user()) {
|
if (!this.user) {
|
||||||
await firstValueFrom(this._userService.create(userData))
|
await firstValueFrom(this._userService.create(userData))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this._toaster.error(null, { error });
|
if (error.status === HttpStatusCode.Conflict) {
|
||||||
|
this._toaster.error(_('add-edit-user.error.email-already-used'));
|
||||||
|
} else {
|
||||||
|
this._toaster.error(_('add-edit-user.error.generic'));
|
||||||
|
}
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await firstValueFrom(this._userService.updateProfile(userData, this.user().id));
|
await firstValueFrom(this._userService.updateProfile(userData, this.user.id));
|
||||||
this.closeDialog.emit(true);
|
this.closeDialog.emit(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
this._dialogService.deleteUsers([this.user().id], () => this.closeDialog.emit(true));
|
this._dialogService.deleteUsers([this.user.id], () => this.closeDialog.emit(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
setRolesRequirements(checked: boolean, role: string): void {
|
setRolesRequirements(checked: boolean, role: string): void {
|
||||||
if (Object.keys(this.#ROLE_REQUIREMENTS).includes(role)) {
|
if (Object.keys(this._ROLE_REQUIREMENTS).includes(role)) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
this.form.patchValue({ [this.#ROLE_REQUIREMENTS[role]]: true });
|
this.form.patchValue({ [this._ROLE_REQUIREMENTS[role]]: true });
|
||||||
this.form.controls[this.#ROLE_REQUIREMENTS[role]].disable();
|
this.form.controls[this._ROLE_REQUIREMENTS[role]].disable();
|
||||||
} else {
|
} else {
|
||||||
this.form.controls[this.#ROLE_REQUIREMENTS[role]].enable();
|
this.form.controls[this._ROLE_REQUIREMENTS[role]].enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,19 +122,18 @@ export class UserDetailsComponent extends BaseFormComponent implements OnInit {
|
|||||||
return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin);
|
return user.id === this._userService.currentUser.id || (userAdmin && !currentUserAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getForm(): UntypedFormGroup {
|
private _getForm(): UntypedFormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
firstName: [this.user()?.firstName, Validators.required],
|
firstName: [this.user?.firstName, Validators.required],
|
||||||
lastName: [this.user()?.lastName, Validators.required],
|
lastName: [this.user?.lastName, Validators.required],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
value: this.user()?.email,
|
value: this.user?.email,
|
||||||
disabled: !!this.user()?.email,
|
disabled: !!this.user?.email,
|
||||||
},
|
},
|
||||||
[Validators.required, Validators.email],
|
[Validators.required, Validators.email],
|
||||||
],
|
],
|
||||||
...this.#rolesControls,
|
...this._rolesControls,
|
||||||
sendSetPasswordMail: [false],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<iqser-help-button *ngIf="!config.IS_DOCUMINE"></iqser-help-button>
|
<iqser-help-button *ngIf="!config.IS_DOCUMINE" [helpButtonKey]="'create_new_entity'"></iqser-help-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||||
|
|||||||
@ -1,16 +1,7 @@
|
|||||||
import { Component, inject, ViewChild } from '@angular/core';
|
import { Component, inject, ViewChild } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import {
|
import { BaseDialogComponent, getConfig, IconButtonTypes } from '@iqser/common-ui';
|
||||||
BaseDialogComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
getConfig,
|
|
||||||
HelpButtonComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
IconButtonTypes,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
|
import { AddEditEntityComponent } from '@shared/components/add-edit-entity/add-edit-entity.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
|
|
||||||
interface DialogData {
|
interface DialogData {
|
||||||
readonly dossierTemplateId: string;
|
readonly dossierTemplateId: string;
|
||||||
@ -19,7 +10,6 @@ interface DialogData {
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './add-entity-dialog.component.html',
|
templateUrl: './add-entity-dialog.component.html',
|
||||||
styleUrls: ['./add-entity-dialog.component.scss'],
|
styleUrls: ['./add-entity-dialog.component.scss'],
|
||||||
imports: [AddEditEntityComponent, TranslateModule, IconButtonComponent, NgIf, CircleButtonComponent, HelpButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class AddEntityDialogComponent extends BaseDialogComponent {
|
export class AddEntityDialogComponent extends BaseDialogComponent {
|
||||||
@ViewChild(AddEditEntityComponent, { static: true }) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
@ViewChild(AddEditEntityComponent, { static: true }) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
||||||
@ -44,7 +34,11 @@ export class AddEntityDialogComponent extends BaseDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
await this._addEditEntityComponent.save();
|
try {
|
||||||
this._dialogRef.close(true);
|
await this._addEditEntityComponent.save();
|
||||||
|
this._dialogRef.close(true);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,9 @@
|
|||||||
<div class="table-header">Key</div>
|
<div class="table-header">Key</div>
|
||||||
<div class="table-header">Value</div>
|
<div class="table-header">Value</div>
|
||||||
|
|
||||||
<ng-container *ngFor="let entry of data.auditEntry.details | keyvalue: originalOrder">
|
<ng-container *ngFor="let entry of data.auditEntry.details | keyvalue : originalOrder">
|
||||||
<div class="bold">{{ entry.key | humanize }}</div>
|
<div class="bold">{{ entry.key | humanize }}</div>
|
||||||
<div>
|
<div>{{ entry.value }}</div>
|
||||||
<pre>{{ entry.value | json }}</pre>
|
|
||||||
</div>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
import { JsonPipe, KeyValue, KeyValuePipe, NgForOf } from '@angular/common';
|
import { KeyValue } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { BaseDialogComponent, CircleButtonComponent, HumanizePipe } from '@iqser/common-ui';
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
import { IAudit } from '@red/domain';
|
import { IAudit } from '@red/domain';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
interface DialogData {
|
interface DialogData {
|
||||||
readonly auditEntry: IAudit;
|
readonly auditEntry: IAudit;
|
||||||
@ -16,7 +15,6 @@ type OrderFn = (a: KeyValue<string, string>, b: KeyValue<string, string>) => num
|
|||||||
templateUrl: './audit-info-dialog.component.html',
|
templateUrl: './audit-info-dialog.component.html',
|
||||||
styleUrls: ['./audit-info-dialog.component.scss'],
|
styleUrls: ['./audit-info-dialog.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [TranslateModule, NgForOf, KeyValuePipe, HumanizePipe, CircleButtonComponent, JsonPipe],
|
|
||||||
})
|
})
|
||||||
export class AuditInfoDialogComponent extends BaseDialogComponent {
|
export class AuditInfoDialogComponent extends BaseDialogComponent {
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@ -1,18 +1,12 @@
|
|||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { HttpStatusCode } from '@angular/common/http';
|
|
||||||
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
|
||||||
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
|
|
||||||
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { DigitalSignatureOption, DigitalSignatureOptions } from '@red/domain';
|
|
||||||
import { DigitalSignatureService } from '../../services/digital-signature.service';
|
|
||||||
import { digitalSignatureDialogTranslations } from '../../translations/digital-signature-dialog-translations';
|
import { digitalSignatureDialogTranslations } from '../../translations/digital-signature-dialog-translations';
|
||||||
import { KmsSignatureConfigurationComponent } from './form/kms-signature-configuration/kms-signature-configuration.component';
|
import { BaseDialogComponent, DetailsRadioOption } from '@iqser/common-ui';
|
||||||
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { PkcsSignatureConfigurationComponent } from './form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
import { PkcsSignatureConfigurationComponent } from './form/pkcs-signature-configuration/pkcs-signature-configuration.component';
|
||||||
|
import { KmsSignatureConfigurationComponent } from './form/kms-signature-configuration/kms-signature-configuration.component';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
|
import { DigitalSignatureOption, DigitalSignatureOptions } from '@red/domain';
|
||||||
|
|
||||||
const DEFAULT_DIALOG_WIDTH = '662px';
|
const DEFAULT_DIALOG_WIDTH = '662px';
|
||||||
const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
||||||
@ -20,18 +14,6 @@ const KMS_SIGNATURE_DIALOG_WIDTH = '810px';
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './configure-certificate-dialog.component.html',
|
templateUrl: './configure-certificate-dialog.component.html',
|
||||||
styleUrls: ['./configure-certificate-dialog.component.scss'],
|
styleUrls: ['./configure-certificate-dialog.component.scss'],
|
||||||
imports: [
|
|
||||||
DetailsRadioComponent,
|
|
||||||
NgIf,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
TranslateModule,
|
|
||||||
PkcsSignatureConfigurationComponent,
|
|
||||||
KmsSignatureConfigurationComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
MatDialogClose,
|
|
||||||
CircleButtonComponent,
|
|
||||||
],
|
|
||||||
providers: [DigitalSignatureService],
|
|
||||||
})
|
})
|
||||||
export class ConfigureCertificateDialogComponent extends BaseDialogComponent {
|
export class ConfigureCertificateDialogComponent extends BaseDialogComponent {
|
||||||
@ViewChild(PkcsSignatureConfigurationComponent) pkcsSignatureConfigurationComponent: PkcsSignatureConfigurationComponent;
|
@ViewChild(PkcsSignatureConfigurationComponent) pkcsSignatureConfigurationComponent: PkcsSignatureConfigurationComponent;
|
||||||
|
|||||||
@ -1,27 +1,20 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
||||||
import { DigitalSignatureOptions, IKmsDigitalSignature, IKmsDigitalSignatureRequest } from '@red/domain';
|
import { DigitalSignatureOptions, IKmsDigitalSignature, IKmsDigitalSignatureRequest } from '@red/domain';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
||||||
import { UploadFileComponent } from '@iqser/common-ui';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-kms-signature-configuration',
|
selector: 'redaction-kms-signature-configuration',
|
||||||
templateUrl: './kms-signature-configuration.component.html',
|
templateUrl: './kms-signature-configuration.component.html',
|
||||||
styleUrls: ['./kms-signature-configuration.component.scss'],
|
styleUrls: ['./kms-signature-configuration.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
|
|
||||||
})
|
})
|
||||||
export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
export class KmsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
||||||
@Input() digitalSignature!: IKmsDigitalSignatureRequest;
|
@Input() digitalSignature!: IKmsDigitalSignatureRequest;
|
||||||
|
|
||||||
constructor(
|
constructor(protected readonly _digitalSignatureService: DigitalSignatureService, private readonly _formBuilder: UntypedFormBuilder) {
|
||||||
protected readonly _digitalSignatureService: DigitalSignatureService,
|
|
||||||
private readonly _formBuilder: UntypedFormBuilder,
|
|
||||||
) {
|
|
||||||
super(_digitalSignatureService, DigitalSignatureOptions.KMS);
|
super(_digitalSignatureService, DigitalSignatureOptions.KMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms';
|
import { UntypedFormBuilder, Validators } from '@angular/forms';
|
||||||
import { DigitalSignatureOptions, IPkcsDigitalSignature, IPkcsDigitalSignatureRequest } from '@red/domain';
|
import { DigitalSignatureOptions, IPkcsDigitalSignature, IPkcsDigitalSignatureRequest } from '@red/domain';
|
||||||
import { lastIndexOfEnd } from '@utils/functions';
|
import { lastIndexOfEnd } from '@utils/functions';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
import { DigitalSignatureService } from '../../../../services/digital-signature.service';
|
||||||
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
import { BaseSignatureConfigurationComponent } from '../base-signature-configuration-component';
|
||||||
import { UploadFileComponent } from '@iqser/common-ui';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-pkcs-signature-configuration',
|
selector: 'redaction-pkcs-signature-configuration',
|
||||||
templateUrl: './pkcs-signature-configuration.component.html',
|
templateUrl: './pkcs-signature-configuration.component.html',
|
||||||
styleUrls: ['./pkcs-signature-configuration.component.scss'],
|
styleUrls: ['./pkcs-signature-configuration.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [UploadFileComponent, ReactiveFormsModule, NgIf, TranslateModule],
|
|
||||||
})
|
})
|
||||||
export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
export class PkcsSignatureConfigurationComponent extends BaseSignatureConfigurationComponent implements OnInit {
|
||||||
@Input() digitalSignature!: IPkcsDigitalSignatureRequest;
|
@Input() digitalSignature!: IPkcsDigitalSignatureRequest;
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
>
|
>
|
||||||
<mat-icon
|
<mat-icon
|
||||||
*ngIf="!form.get('color').value || form.get('color').value?.length === 0"
|
*ngIf="!form.get('color').value || form.get('color').value?.length === 0"
|
||||||
svgIcon="iqser:color-picker"
|
svgIcon="red:color-picker"
|
||||||
></mat-icon>
|
></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { DefaultColorType } from '@red/domain';
|
import { DefaultColorType } from '@red/domain';
|
||||||
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
import { BaseDialogComponent, IconButtonTypes } from '@iqser/common-ui';
|
||||||
import { ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ColorPickerModule } from 'ngx-color-picker';
|
|
||||||
import { MatIcon } from '@angular/material/icon';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
|
|
||||||
interface IEditColorData {
|
interface IEditColorData {
|
||||||
colorKey: DefaultColorType;
|
colorKey: DefaultColorType;
|
||||||
@ -20,7 +16,6 @@ interface IEditColorData {
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './edit-color-dialog.component.html',
|
templateUrl: './edit-color-dialog.component.html',
|
||||||
styleUrls: ['./edit-color-dialog.component.scss'],
|
styleUrls: ['./edit-color-dialog.component.scss'],
|
||||||
imports: [ReactiveFormsModule, TranslateModule, ColorPickerModule, MatIcon, NgIf, IconButtonComponent, CircleButtonComponent],
|
|
||||||
})
|
})
|
||||||
export class EditColorDialogComponent extends BaseDialogComponent {
|
export class EditColorDialogComponent extends BaseDialogComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|||||||
@ -1,15 +1,13 @@
|
|||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { ReactiveFormsModule, Validators } from '@angular/forms';
|
import { Validators } from '@angular/forms';
|
||||||
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { BaseDialogComponent, CircleButtonComponent, IconButtonComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
import { ISmtpConfiguration } from '@red/domain';
|
import { ISmtpConfiguration } from '@red/domain';
|
||||||
import { getCurrentUser } from '@users/user.service';
|
import { getCurrentUser } from '@users/user.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-smtp-auth-dialog',
|
selector: 'redaction-smtp-auth-dialog',
|
||||||
templateUrl: './smtp-auth-dialog.component.html',
|
templateUrl: './smtp-auth-dialog.component.html',
|
||||||
imports: [ReactiveFormsModule, TranslateModule, IconButtonComponent, CircleButtonComponent, MatDialogClose],
|
|
||||||
})
|
})
|
||||||
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
export class SmtpAuthDialogComponent extends BaseDialogComponent {
|
||||||
readonly #currentUser = getCurrentUser();
|
readonly #currentUser = getCurrentUser();
|
||||||
|
|||||||
@ -1,13 +1,11 @@
|
|||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
import { IconButtonTypes } from '@iqser/common-ui';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './upload-dictionary-dialog.component.html',
|
templateUrl: './upload-dictionary-dialog.component.html',
|
||||||
styleUrls: ['./upload-dictionary-dialog.component.scss'],
|
styleUrls: ['./upload-dictionary-dialog.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [IconButtonComponent, TranslateModule],
|
|
||||||
})
|
})
|
||||||
export class UploadDictionaryDialogComponent {
|
export class UploadDictionaryDialogComponent {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
|||||||
@ -23,3 +23,9 @@ form {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-info {
|
||||||
|
background: var(--iqser-light);
|
||||||
|
border: 1px solid var(--iqser-grey-1);
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,34 +1,17 @@
|
|||||||
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
import { Component, inject, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import { IqserPermissionsService, ListingComponent, listingProvidersFactory, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||||
CircleButtonComponent,
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
HumanizePipe,
|
|
||||||
IqserListingModule,
|
|
||||||
IqserPermissionsService,
|
|
||||||
ListingComponent,
|
|
||||||
listingProvidersFactory,
|
|
||||||
LoadingService,
|
|
||||||
TableColumnConfig,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { getCurrentUser, InitialsAvatarComponent } from '@iqser/common-ui/lib/users';
|
|
||||||
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest, User } from '@red/domain';
|
import { Audit, IAudit, IAuditResponse, IAuditSearchRequest, User } from '@red/domain';
|
||||||
import { RouterHistoryService } from '@services/router-history.service';
|
import { RouterHistoryService } from '@services/router-history.service';
|
||||||
import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
|
import { auditCategoriesTranslations } from '@translations/audit-categories-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
import { applyIntervalConstraints } from '@utils/date-inputs-utils';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import { Dayjs } from 'dayjs';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
import { AuditService } from '../../services/audit.service';
|
import { AuditService } from '../../services/audit.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { PaginationComponent } from '@common-ui/pagination';
|
|
||||||
import { MatFormField, MatSuffix } from '@angular/material/form-field';
|
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
|
||||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
||||||
import { MatIcon } from '@angular/material/icon';
|
|
||||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
|
||||||
|
|
||||||
const PAGE_SIZE = 50;
|
const PAGE_SIZE = 50;
|
||||||
|
|
||||||
@ -36,26 +19,6 @@ const PAGE_SIZE = 50;
|
|||||||
templateUrl: './audit-screen.component.html',
|
templateUrl: './audit-screen.component.html',
|
||||||
styleUrls: ['./audit-screen.component.scss'],
|
styleUrls: ['./audit-screen.component.scss'],
|
||||||
providers: listingProvidersFactory(AuditScreenComponent),
|
providers: listingProvidersFactory(AuditScreenComponent),
|
||||||
imports: [
|
|
||||||
IqserListingModule,
|
|
||||||
TranslateModule,
|
|
||||||
PaginationComponent,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
MatFormField,
|
|
||||||
MatOption,
|
|
||||||
NgForOf,
|
|
||||||
InitialsAvatarComponent,
|
|
||||||
NgIf,
|
|
||||||
MatIcon,
|
|
||||||
MatSuffix,
|
|
||||||
AsyncPipe,
|
|
||||||
DatePipe,
|
|
||||||
HumanizePipe,
|
|
||||||
CircleButtonComponent,
|
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
|
||||||
MatDatepickerModule,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
|
export class AuditScreenComponent extends ListingComponent<Audit> implements OnInit, OnDestroy {
|
||||||
private _previousFrom: Dayjs;
|
private _previousFrom: Dayjs;
|
||||||
@ -63,7 +26,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
readonly ALL_CATEGORIES = 'allCategories';
|
readonly ALL_CATEGORIES = 'allCategories';
|
||||||
readonly ALL_USERS = _('audit-screen.all-users');
|
readonly ALL_USERS = _('audit-screen.all-users');
|
||||||
readonly translations = auditCategoriesTranslations;
|
readonly translations = auditCategoriesTranslations;
|
||||||
readonly form: UntypedFormGroup = this.#getForm();
|
readonly form: UntypedFormGroup = this._getForm();
|
||||||
readonly routerHistoryService = inject(RouterHistoryService);
|
readonly routerHistoryService = inject(RouterHistoryService);
|
||||||
readonly permissionsService = inject(IqserPermissionsService);
|
readonly permissionsService = inject(IqserPermissionsService);
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
@ -96,17 +59,17 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
}
|
}
|
||||||
|
|
||||||
async pageChanged(page: number) {
|
async pageChanged(page: number) {
|
||||||
await this.#fetchData(page);
|
await this._fetchData(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.sortingService.setSortingOption({ column: 'recordDate', order: 'desc' });
|
this.sortingService.setSortingOption({ column: 'recordDate', order: 'desc' });
|
||||||
await this.#fetchData();
|
await this._fetchData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterChange() {
|
async filterChange() {
|
||||||
if (!this.#updateDateFilters(this.form.value)) {
|
if (!this._updateDateFilters(this.form.value)) {
|
||||||
await this.#fetchData();
|
await this._fetchData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +77,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
this._dialogService.openDialog('auditInfo', { auditEntry: log });
|
this._dialogService.openDialog('auditInfo', { auditEntry: log });
|
||||||
}
|
}
|
||||||
|
|
||||||
#getForm(): UntypedFormGroup {
|
private _getForm(): UntypedFormGroup {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
category: [this.ALL_CATEGORIES],
|
category: [this.ALL_CATEGORIES],
|
||||||
userId: [this.ALL_USERS],
|
userId: [this.ALL_USERS],
|
||||||
@ -123,7 +86,7 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateDateFilters(value): boolean {
|
private _updateDateFilters(value): boolean {
|
||||||
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
if (applyIntervalConstraints(value, this._previousFrom, this._previousTo, this.form, 'from', 'to')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -133,14 +96,16 @@ export class AuditScreenComponent extends ListingComponent<Audit> implements OnI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async #fetchData(page?: number) {
|
private async _fetchData(page?: number) {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const promises = [];
|
const promises = [];
|
||||||
const category = this.form.get('category').value;
|
const category = this.form.get('category').value;
|
||||||
const userId = this.form.get('userId').value;
|
const userId = this.form.get('userId').value;
|
||||||
const from = this.form.get('from').value ? dayjs(this.form.get('from').value).startOf('day').toISOString() : null;
|
const from = this.form.get('from').value;
|
||||||
const to = this.form.get('to').value ? dayjs(this.form.get('to').value).endOf('day').toISOString() : null;
|
let to = this.form.get('to').value;
|
||||||
|
if (to) {
|
||||||
|
to = to.clone().add(1, 'd');
|
||||||
|
}
|
||||||
const logsRequestBody: IAuditSearchRequest = {
|
const logsRequestBody: IAuditSearchRequest = {
|
||||||
pageSize: PAGE_SIZE,
|
pageSize: PAGE_SIZE,
|
||||||
page: page,
|
page: page,
|
||||||
|
|||||||
@ -1,130 +0,0 @@
|
|||||||
@if (componentDefinitions$ | async; as componentDefinitions) {
|
|
||||||
<div class="content-container">
|
|
||||||
<div class="content-header">
|
|
||||||
<div class="header-title">
|
|
||||||
<span
|
|
||||||
class="all-caps-label"
|
|
||||||
[innerHTML]="'component-definitions.title' | translate: { length: componentDefinitions.length }"
|
|
||||||
></span>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
@if (permissionsService.canEditEntities()) {
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="createEmptyComponentDefinition()"
|
|
||||||
[label]="'component-definitions.add-new' | translate"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
icon="iqser:plus"
|
|
||||||
></iqser-icon-button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div class="components-list">
|
|
||||||
<div class="header">
|
|
||||||
<div class="item-content">
|
|
||||||
<div class="all-caps-label">{{ 'component-definitions.columns.position' | translate }}</div>
|
|
||||||
<div class="all-caps-label">{{ 'component-definitions.columns.name' | translate }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div (cdkDropListDropped)="drop($event)" cdkDropList class="list-content">
|
|
||||||
@for (component of componentDefinitions; track component) {
|
|
||||||
<div
|
|
||||||
class="list-item"
|
|
||||||
[class.selected]="selectedComponent?.id === component.id"
|
|
||||||
cdkDrag
|
|
||||||
(click)="selectComponent(component)"
|
|
||||||
>
|
|
||||||
<div class="item-content">
|
|
||||||
<div class="table-item-title heading">
|
|
||||||
<mat-icon cdkDragHandle class="draggable" svgIcon="red:draggable-dots"></mat-icon>
|
|
||||||
<span> {{ component.rank }} </span>
|
|
||||||
</div>
|
|
||||||
<div class="table-item-title heading">{{ component.displayName }}</div>
|
|
||||||
<div class="right-content">
|
|
||||||
@if (permissionsService.canEditEntities()) {
|
|
||||||
<iqser-circle-button
|
|
||||||
[class]="'delete-component-definition'"
|
|
||||||
(action)="deleteComponent(component.id)"
|
|
||||||
[tooltip]="'trash.action.delete' | translate"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
}
|
|
||||||
<mat-icon
|
|
||||||
[class.not-visible]="selectedComponent?.id !== component.id"
|
|
||||||
class="arrow-right"
|
|
||||||
svgIcon="red:arrow-right"
|
|
||||||
></mat-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if (selectedComponent) {
|
|
||||||
<section class="dialog">
|
|
||||||
<div class="dialog-header">
|
|
||||||
@if (selectedComponent.id) {
|
|
||||||
<div
|
|
||||||
class="heading-l"
|
|
||||||
[innerHTML]="'component-definitions.edit-title' | translate: { displayName: selectedComponent.displayName }"
|
|
||||||
></div>
|
|
||||||
} @else {
|
|
||||||
<div class="heading-l" [innerHTML]="'component-definitions.add-title' | translate"></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
@if (form) {
|
|
||||||
<form (submit)="save()" [formGroup]="form">
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="iqser-input-group w-300 required">
|
|
||||||
<label [translate]="'component-definitions.form.display-name'"></label>
|
|
||||||
<input
|
|
||||||
formControlName="displayName"
|
|
||||||
name="displayName"
|
|
||||||
placeholder="{{ 'component-definitions.form.display-name-placeholder' | translate }}"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label [translate]="'component-definitions.form.description'"></label>
|
|
||||||
<textarea
|
|
||||||
[placeholder]="'component-definitions.form.description-placeholder' | translate"
|
|
||||||
formControlName="description"
|
|
||||||
iqserHasScrollbar
|
|
||||||
name="description"
|
|
||||||
rows="5"
|
|
||||||
type="text"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group w-450">
|
|
||||||
<label [translate]="'component-definitions.form.technical-name-label'"></label>
|
|
||||||
<span class="technical-name"> {{ technicalName }} </span>
|
|
||||||
<label [translate]="'component-definitions.form.autogenerated-label'"></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
[disabled]="disabled"
|
|
||||||
[label]="'component-definitions.actions.save' | translate"
|
|
||||||
[submit]="true"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
></iqser-icon-button>
|
|
||||||
@if (selectedComponent.id) {
|
|
||||||
<div
|
|
||||||
(click)="initForm()"
|
|
||||||
[class.disabled]="disabled"
|
|
||||||
[translate]="'component-definitions.actions.revert'"
|
|
||||||
class="all-caps-label cancel"
|
|
||||||
></div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
}
|
|
||||||
</section>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@ -1,145 +0,0 @@
|
|||||||
%item-content-style {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
margin: 5px;
|
|
||||||
|
|
||||||
div:first-child {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.draggable {
|
|
||||||
cursor: grab;
|
|
||||||
transform: scale(0.7);
|
|
||||||
width: 40px;
|
|
||||||
margin-left: -30px;
|
|
||||||
|
|
||||||
::ng-deep svg {
|
|
||||||
fill: var(--iqser-grey-7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-content {
|
|
||||||
visibility: hidden;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: flex-end;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
iqser-circle-button {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-right {
|
|
||||||
transform: scale(0.7);
|
|
||||||
|
|
||||||
&.not-visible {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
background-color: var(--iqser-grey-6);
|
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.content-header {
|
|
||||||
display: flex;
|
|
||||||
height: 50px;
|
|
||||||
background-color: var(--iqser-grey-6);
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 24px;
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.components-list {
|
|
||||||
flex: 1;
|
|
||||||
background-color: var(--iqser-white);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.header {
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-content {
|
|
||||||
overflow-y: auto;
|
|
||||||
height: calc(100vh - 30px);
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-item {
|
|
||||||
height: 80px;
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header,
|
|
||||||
.list-item {
|
|
||||||
display: flex;
|
|
||||||
border-bottom: 1px solid var(--iqser-separator);
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
@extend %item-content-style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-item:hover,
|
|
||||||
.selected {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.item-content {
|
|
||||||
background-color: var(--iqser-grey-8);
|
|
||||||
|
|
||||||
.right-content {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.list-item:hover > .item-content > .right-content > iqser-circle-button {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
section {
|
|
||||||
background-color: var(--iqser-white);
|
|
||||||
border-radius: 8px;
|
|
||||||
width: 700px;
|
|
||||||
height: 470px;
|
|
||||||
margin-right: 150px;
|
|
||||||
|
|
||||||
.technical-name {
|
|
||||||
min-height: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdk-drag-preview {
|
|
||||||
.list-item {
|
|
||||||
transition: background 0.3s ease;
|
|
||||||
}
|
|
||||||
.item-content {
|
|
||||||
@extend %item-content-style;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,138 +0,0 @@
|
|||||||
import { Component, OnInit, signal } from '@angular/core';
|
|
||||||
import {
|
|
||||||
BaseFormComponent,
|
|
||||||
CircleButtonComponent,
|
|
||||||
HasScrollbarDirective,
|
|
||||||
IconButtonComponent,
|
|
||||||
listingProvidersFactory,
|
|
||||||
LoadingService,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { ComponentDefinitionsService } from '@services/entity-services/component-definitions.service';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { getParam } from '@common-ui/utils';
|
|
||||||
import { DOSSIER_TEMPLATE_ID, IComponentDefinition } from '@red/domain';
|
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { MatIcon } from '@angular/material/icon';
|
|
||||||
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
||||||
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './component-definitions.component.html',
|
|
||||||
styleUrls: ['./component-definitions.component.scss'],
|
|
||||||
providers: listingProvidersFactory(ComponentDefinitionsComponent),
|
|
||||||
imports: [
|
|
||||||
IconButtonComponent,
|
|
||||||
TranslateModule,
|
|
||||||
CommonModule,
|
|
||||||
MatIcon,
|
|
||||||
CdkDragHandle,
|
|
||||||
CdkDropList,
|
|
||||||
CdkDrag,
|
|
||||||
FormsModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
CircleButtonComponent,
|
|
||||||
HasScrollbarDirective,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export default class ComponentDefinitionsComponent extends BaseFormComponent implements OnInit {
|
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
|
||||||
readonly #componentDefinitions = signal<IComponentDefinition[]>([]);
|
|
||||||
protected readonly componentDefinitions$ = toObservable(this.#componentDefinitions);
|
|
||||||
protected selectedComponent: IComponentDefinition | null = null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
private readonly _componentDefinitionsService: ComponentDefinitionsService,
|
|
||||||
private readonly _formBuilder: FormBuilder,
|
|
||||||
private readonly _dialogService: AdminDialogService,
|
|
||||||
protected readonly permissionsService: PermissionsService,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this._loadingService.stop();
|
|
||||||
await this.#loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
async #loadData() {
|
|
||||||
const componentDefinitions = await firstValueFrom(
|
|
||||||
this._componentDefinitionsService.getComponentDefinitions(this.#dossierTemplateId),
|
|
||||||
);
|
|
||||||
this.#componentDefinitions.set(componentDefinitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async createEmptyComponentDefinition() {
|
|
||||||
this.selectedComponent = {
|
|
||||||
displayName: '',
|
|
||||||
description: '',
|
|
||||||
} as IComponentDefinition;
|
|
||||||
this.initForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
async save() {
|
|
||||||
if (this.selectedComponent.id) {
|
|
||||||
const component = { ...this.form.getRawValue(), id: this.selectedComponent.id };
|
|
||||||
await firstValueFrom(this._componentDefinitionsService.updateComponentDefinition(this.#dossierTemplateId, component));
|
|
||||||
} else {
|
|
||||||
const component = {
|
|
||||||
...this.form.getRawValue(),
|
|
||||||
dossierTemplateId: this.#dossierTemplateId,
|
|
||||||
technicalName: this.technicalName,
|
|
||||||
};
|
|
||||||
await firstValueFrom(this._componentDefinitionsService.createComponentDefinition(this.#dossierTemplateId, component));
|
|
||||||
}
|
|
||||||
await this.#loadData();
|
|
||||||
this.selectComponent();
|
|
||||||
await this.initForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
async drop(event: CdkDragDrop<string>) {
|
|
||||||
if (event.isPointerOverContainer) {
|
|
||||||
moveItemInArray(this.#componentDefinitions(), event.previousIndex, event.currentIndex);
|
|
||||||
|
|
||||||
const componentIds = this.#componentDefinitions().map(c => c.id);
|
|
||||||
const componentDefinitions = await firstValueFrom(
|
|
||||||
this._componentDefinitionsService.reorderComponentDefinitions(this.#dossierTemplateId, componentIds),
|
|
||||||
);
|
|
||||||
this.#componentDefinitions.set(componentDefinitions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteComponent(componentId: string) {
|
|
||||||
this._dialogService.openDialog('confirm', null, async () => {
|
|
||||||
await firstValueFrom(this._componentDefinitionsService.deleteComponentDefinitions(this.#dossierTemplateId, [componentId]));
|
|
||||||
await this.#loadData();
|
|
||||||
this.selectedComponent = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initForm() {
|
|
||||||
this.form = this.#getForm();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm() {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
displayName: [this.selectedComponent?.displayName, Validators.required],
|
|
||||||
description: [this.selectedComponent?.description],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectComponent(component?: IComponentDefinition) {
|
|
||||||
if (component && this.selectedComponent?.id !== component.id) {
|
|
||||||
this.selectedComponent = component;
|
|
||||||
this.initForm();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.selectedComponent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get technicalName() {
|
|
||||||
return this.selectedComponent.technicalName ?? this.form.get('displayName')?.value?.toLowerCase().trim().replace(/\s+/g, '_');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
<section class="dialog">
|
|
||||||
<div
|
|
||||||
[innerHTML]="'add-edit-component-mapping.dialog.title' | translate: { type: data.mapping ? 'edit' : 'add' }"
|
|
||||||
class="dialog-header heading-l"
|
|
||||||
></div>
|
|
||||||
<form (submit)="save()" [formGroup]="form">
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="row">
|
|
||||||
<div class="iqser-input-group required w-300">
|
|
||||||
<label translate="add-edit-component-mapping.form.name"></label>
|
|
||||||
<input
|
|
||||||
[placeholder]="'add-edit-component-mapping.form.name-placeholder' | translate"
|
|
||||||
formControlName="name"
|
|
||||||
name="label"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group w-100 version" *ngIf="data?.mapping?.id">
|
|
||||||
<label translate="add-edit-component-mapping.form.version"></label>
|
|
||||||
<span> {{ data.mapping.version }} </span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group required">
|
|
||||||
<label translate="add-edit-component-mapping.form.file"></label>
|
|
||||||
<iqser-upload-file (fileChanged)="changeFile($event)" [file]="activeFile" [accept]="'.csv'" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
[matTooltip]="'add-edit-component-mapping.disabled-file-options' | translate"
|
|
||||||
[matTooltipDisabled]="!form.get('encoding')?.disabled"
|
|
||||||
[matTooltipPosition]="'above'"
|
|
||||||
>
|
|
||||||
<div class="iqser-input-group required w-150">
|
|
||||||
<label translate="add-edit-component-mapping.form.delimiter"></label>
|
|
||||||
<input
|
|
||||||
[placeholder]="'add-edit-component-mapping.form.delimiter-placeholder' | translate"
|
|
||||||
formControlName="delimiter"
|
|
||||||
name="delimiter"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="iqser-input-group required w-150">
|
|
||||||
<label translate="add-edit-component-mapping.form.quote-char"></label>
|
|
||||||
<input
|
|
||||||
[placeholder]="'add-edit-component-mapping.form.quote-char-placeholder' | translate"
|
|
||||||
formControlName="quoteChar"
|
|
||||||
name="quoteChar"
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group required w-150">
|
|
||||||
<label translate="add-edit-component-mapping.form.encoding-type"></label>
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="encoding">
|
|
||||||
<mat-option *ngFor="let type of encodingTypeOptions" [value]="type">
|
|
||||||
{{ translations[type] | translate }}
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="save()"
|
|
||||||
[disabled]="disabled"
|
|
||||||
[label]="'add-edit-component-mapping.actions.save' | translate"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<iqser-circle-button class="dialog-close" icon="iqser:close" mat-dialog-close></iqser-circle-button>
|
|
||||||
</section>
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
.row {
|
|
||||||
display: flex;
|
|
||||||
margin-top: 14px;
|
|
||||||
|
|
||||||
> *:not(:last-child) {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iqser-input-group {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version {
|
|
||||||
margin-left: 50px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.row:last-child {
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { CircleButtonComponent, IconButtonComponent, IqserDialogComponent, UploadFileComponent } from '@iqser/common-ui';
|
|
||||||
import { FileAttributeEncodingTypes, IComponentMapping } from '@red/domain';
|
|
||||||
import { FormBuilder, ReactiveFormsModule, UntypedFormGroup, Validators } from '@angular/forms';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
|
||||||
import { MatOption } from '@angular/material/autocomplete';
|
|
||||||
import { MatSelect } from '@angular/material/select';
|
|
||||||
import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { ComponentMappingsService } from '@services/entity-services/component-mappings.service';
|
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
|
||||||
|
|
||||||
interface DialogData {
|
|
||||||
dossierTemplateId: string;
|
|
||||||
mapping: IComponentMapping;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DialogResult {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
file: Blob;
|
|
||||||
encoding: string;
|
|
||||||
delimiter: string;
|
|
||||||
quoteChar: string;
|
|
||||||
fileName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './add-edit-component-mapping-dialog.component.html',
|
|
||||||
styleUrls: ['./add-edit-component-mapping-dialog.component.scss'],
|
|
||||||
imports: [
|
|
||||||
TranslateModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
NgIf,
|
|
||||||
MatFormFieldModule,
|
|
||||||
NgForOf,
|
|
||||||
CircleButtonComponent,
|
|
||||||
MatDialogModule,
|
|
||||||
MatOption,
|
|
||||||
MatSelect,
|
|
||||||
IconButtonComponent,
|
|
||||||
UploadFileComponent,
|
|
||||||
MatTooltip,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class AddEditComponentMappingDialogComponent
|
|
||||||
extends IqserDialogComponent<AddEditComponentMappingDialogComponent, DialogData, DialogResult>
|
|
||||||
implements OnInit
|
|
||||||
{
|
|
||||||
protected readonly encodingTypeOptions = Object.keys(FileAttributeEncodingTypes);
|
|
||||||
protected readonly translations = fileAttributeEncodingTypesTranslations;
|
|
||||||
activeFile: File;
|
|
||||||
form!: UntypedFormGroup;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _formBuilder: FormBuilder,
|
|
||||||
private readonly _componentMappingService: ComponentMappingsService,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this.form = this.#getForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
if (this.data.mapping?.fileName) {
|
|
||||||
this.activeFile = { name: this.data.mapping.fileName } as File;
|
|
||||||
const fileContent = await firstValueFrom(
|
|
||||||
this._componentMappingService.getComponentMappingFile(this.data.dossierTemplateId, this.data.mapping.id),
|
|
||||||
);
|
|
||||||
const file = new Blob([fileContent.body as Blob], { type: 'text/csv' });
|
|
||||||
this.form.get('file').setValue(file);
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
this.#disableEncodingAndQuoteCharAndDelimiter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
changeFile(file: File) {
|
|
||||||
this.form.get('file').setValue(file);
|
|
||||||
this.form.get('fileName').setValue(file?.name);
|
|
||||||
this.#enableEncodingAndQuoteCharAndDelimiter();
|
|
||||||
}
|
|
||||||
|
|
||||||
save() {
|
|
||||||
this.dialogRef.close({ ...this.data.mapping, ...this.form.getRawValue() });
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm(): UntypedFormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
name: [this.data?.mapping?.name, Validators.required],
|
|
||||||
file: [null, Validators.required],
|
|
||||||
fileName: [this.data?.mapping?.fileName, Validators.required],
|
|
||||||
encoding: this.encodingTypeOptions.find(e => e === this.data?.mapping?.encoding) ?? this.encodingTypeOptions[0],
|
|
||||||
delimiter: [this.data?.mapping?.delimiter ?? ',', Validators.required],
|
|
||||||
quoteChar: [this.data?.mapping?.quoteChar ?? '"', Validators.required],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#disableEncodingAndQuoteCharAndDelimiter() {
|
|
||||||
this.form.get('encoding').disable();
|
|
||||||
this.form.get('delimiter').disable();
|
|
||||||
this.form.get('quoteChar').disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
#enableEncodingAndQuoteCharAndDelimiter() {
|
|
||||||
this.form.get('encoding').enable();
|
|
||||||
this.form.get('delimiter').enable();
|
|
||||||
this.form.get('quoteChar').enable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
<div class="content-container" *ngIf="context$ | async as context">
|
|
||||||
<iqser-table
|
|
||||||
(noDataAction)="openAddEditComponentMappingDialog()"
|
|
||||||
[headerTemplate]="headerTemplate"
|
|
||||||
[itemSize]="80"
|
|
||||||
[tableColumnConfigs]="tableColumnConfigs"
|
|
||||||
[noDataText]="'component-mappings-screen.no-data.title' | translate"
|
|
||||||
[showNoDataButton]="roles.componentMappings.write && currentUser.isAdmin"
|
|
||||||
[noDataButtonLabel]="'component-mappings-screen.no-data.action' | translate"
|
|
||||||
emptyColumnWidth="2fr"
|
|
||||||
noDataIcon="iqser:csv"
|
|
||||||
></iqser-table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ng-template #headerTemplate>
|
|
||||||
<div class="table-header-actions">
|
|
||||||
<iqser-input-with-action
|
|
||||||
[(value)]="searchService.searchValue"
|
|
||||||
[placeholder]="'component-mappings-screen.search' | translate"
|
|
||||||
></iqser-input-with-action>
|
|
||||||
<div class="actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
(action)="openAddEditComponentMappingDialog()"
|
|
||||||
*allow="roles.componentMappings.write; if: currentUser.isAdmin"
|
|
||||||
[label]="'component-mappings-screen.add-new' | translate"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
icon="iqser:plus"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<ng-template #tableItemTemplate let-entity="entity">
|
|
||||||
<div>
|
|
||||||
<div class="label cell">
|
|
||||||
<span>{{ entity.name }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<span>{{ entity.version }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<span>{{ entity.numberOfLines }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell" [matTooltip]="entity.columnLabelsString" [matTooltipPosition]="'above'" [matTooltipClass]="'custom-tooltip'">
|
|
||||||
<span class="ellipsis">{{ entity.columnLabelsString }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="cell">
|
|
||||||
<div *allow="roles.componentMappings.write; if: currentUser.isAdmin" class="action-buttons">
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="openAddEditComponentMappingDialog(entity)"
|
|
||||||
[tooltip]="'component-mappings-screen.action.edit' | translate"
|
|
||||||
icon="iqser:edit"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="downloadMapping(entity)"
|
|
||||||
[tooltip]="'component-mappings-screen.action.download' | translate"
|
|
||||||
icon="iqser:download"
|
|
||||||
></iqser-circle-button>
|
|
||||||
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="openDeleteComponentMappingDialog(entity)"
|
|
||||||
[tooltip]="'component-mappings-screen.action.delete' | translate"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ng-template>
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
.ellipsis {
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
::ng-deep .custom-tooltip {
|
|
||||||
max-width: 300px;
|
|
||||||
white-space: normal;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
|
||||||
import { getCurrentUser } from '@common-ui/users';
|
|
||||||
import { getParam } from '@common-ui/utils';
|
|
||||||
import {
|
|
||||||
CircleButtonComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
IconButtonTypes,
|
|
||||||
IqserAllowDirective,
|
|
||||||
IqserDialog,
|
|
||||||
IqserListingModule,
|
|
||||||
ListingComponent,
|
|
||||||
listingProvidersFactory,
|
|
||||||
LoadingService,
|
|
||||||
TableColumnConfig,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { ComponentMapping, DOSSIER_TEMPLATE_ID, User } from '@red/domain';
|
|
||||||
import { ComponentMappingsService } from '@services/entity-services/component-mappings.service';
|
|
||||||
import { defaultColorsTranslations } from '@translations/default-colors-translations';
|
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { combineLatest, firstValueFrom } from 'rxjs';
|
|
||||||
import { map, tap } from 'rxjs/operators';
|
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
|
||||||
import { AddEditComponentMappingDialogComponent } from './add-edit-component-mapping-dialog/add-edit-component-mapping-dialog.component';
|
|
||||||
import { download } from '@utils/file-download-utils';
|
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './component-mappings-screen.component.html',
|
|
||||||
styleUrls: ['./component-mappings-screen.component.scss'],
|
|
||||||
providers: listingProvidersFactory(ComponentMappingsScreenComponent),
|
|
||||||
imports: [
|
|
||||||
AsyncPipe,
|
|
||||||
NgIf,
|
|
||||||
CircleButtonComponent,
|
|
||||||
TranslateModule,
|
|
||||||
IqserListingModule,
|
|
||||||
InputWithActionComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
IqserAllowDirective,
|
|
||||||
MatTooltip,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export default class ComponentMappingsScreenComponent extends ListingComponent<ComponentMapping> implements OnInit {
|
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
|
||||||
protected readonly context$;
|
|
||||||
protected readonly currentUser = getCurrentUser<User>();
|
|
||||||
protected readonly translations = defaultColorsTranslations;
|
|
||||||
protected readonly roles = Roles;
|
|
||||||
protected readonly iconButtonTypes = IconButtonTypes;
|
|
||||||
tableColumnConfigs: readonly TableColumnConfig<ComponentMapping>[] = [
|
|
||||||
{ label: _('component-mappings-screen.table-col-names.name'), sortByKey: 'searchKey' },
|
|
||||||
{ label: _('component-mappings-screen.table-col-names.version') },
|
|
||||||
{ label: _('component-mappings-screen.table-col-names.number-of-lines') },
|
|
||||||
{ label: _('component-mappings-screen.table-col-names.column-labels') },
|
|
||||||
];
|
|
||||||
readonly tableHeaderLabel = _('component-mappings-screen.table-header.title');
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _loadingService: LoadingService,
|
|
||||||
private readonly _componentMappingService: ComponentMappingsService,
|
|
||||||
private readonly _iqserDialog: IqserDialog,
|
|
||||||
private readonly _dialogService: AdminDialogService,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this.context$ = this.loadData$();
|
|
||||||
}
|
|
||||||
|
|
||||||
loadData$() {
|
|
||||||
return combineLatest([this._componentMappingService.getComponentMappings(this.#dossierTemplateId)]).pipe(
|
|
||||||
map(([mappingList]) => mappingList.componentMappingList),
|
|
||||||
tap(mappings => this.entitiesService.setEntities(mappings)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
async openAddEditComponentMappingDialog(mapping?: ComponentMapping) {
|
|
||||||
const dialog = this._iqserDialog.openDefault(AddEditComponentMappingDialogComponent, {
|
|
||||||
data: {
|
|
||||||
dossierTemplateId: this.#dossierTemplateId,
|
|
||||||
mapping,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const result = await dialog.result();
|
|
||||||
if (result) {
|
|
||||||
this._loadingService.start();
|
|
||||||
const { id, name, encoding, delimiter, fileName, quoteChar } = result;
|
|
||||||
const newMapping = { id, name, encoding, delimiter, fileName, quoteChar };
|
|
||||||
await firstValueFrom(
|
|
||||||
this._componentMappingService.createUpdateComponentMapping(this.#dossierTemplateId, newMapping, result.file),
|
|
||||||
);
|
|
||||||
await firstValueFrom(this.loadData$());
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async downloadMapping(entity: ComponentMapping) {
|
|
||||||
const componentMappingFile = await firstValueFrom(
|
|
||||||
this._componentMappingService.getComponentMappingFile(this.#dossierTemplateId, entity.id),
|
|
||||||
);
|
|
||||||
download(componentMappingFile, entity.fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
openDeleteComponentMappingDialog(entity: ComponentMapping) {
|
|
||||||
this._dialogService.openDialog('confirm', null, async confirmation => {
|
|
||||||
if (confirmation) {
|
|
||||||
this._loadingService.start();
|
|
||||||
await firstValueFrom(this._componentMappingService.deleteComponentMapping(this.#dossierTemplateId, entity.id));
|
|
||||||
await firstValueFrom(this.loadData$());
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,6 +1,29 @@
|
|||||||
<div class="content-container" *ngIf="context$ | async as context">
|
<section *ngIf="context$ | async as context">
|
||||||
<iqser-table [itemSize]="80" [tableColumnConfigs]="tableColumnConfigs" emptyColumnWidth="2fr"></iqser-table>
|
<div class="page-header">
|
||||||
</div>
|
<redaction-dossier-template-breadcrumbs class="flex-1"></redaction-dossier-template-breadcrumbs>
|
||||||
|
|
||||||
|
<div class="flex-1 actions">
|
||||||
|
<redaction-dossier-template-actions></redaction-dossier-template-actions>
|
||||||
|
|
||||||
|
<iqser-circle-button
|
||||||
|
[routerLink]="['../..']"
|
||||||
|
[tooltip]="'common.close' | translate"
|
||||||
|
icon="iqser:close"
|
||||||
|
tooltipPosition="below"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content-inner">
|
||||||
|
<div class="overlay-shadow"></div>
|
||||||
|
|
||||||
|
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
|
||||||
|
|
||||||
|
<div class="content-container">
|
||||||
|
<iqser-table [itemSize]="80" [tableColumnConfigs]="tableColumnConfigs" emptyColumnWidth="2fr"></iqser-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<ng-template #tableItemTemplate let-entity="entity">
|
<ng-template #tableItemTemplate let-entity="entity">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -1,16 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import { getConfig, IListable, ListingComponent, listingProvidersFactory, LoadingService, TableColumnConfig } from '@iqser/common-ui';
|
||||||
CircleButtonComponent,
|
|
||||||
getConfig,
|
|
||||||
IListable,
|
|
||||||
IqserAllowDirective,
|
|
||||||
IqserListingModule,
|
|
||||||
ListingComponent,
|
|
||||||
listingProvidersFactory,
|
|
||||||
LoadingService,
|
|
||||||
TableColumnConfig,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { getParam } from '@iqser/common-ui/lib/utils';
|
import { getParam } from '@iqser/common-ui/lib/utils';
|
||||||
import { DefaultColorTypes, DOSSIER_TEMPLATE_ID, User } from '@red/domain';
|
import { DefaultColorTypes, DOSSIER_TEMPLATE_ID, User } from '@red/domain';
|
||||||
@ -20,8 +10,6 @@ import { Roles } from '@users/roles';
|
|||||||
import { combineLatest } from 'rxjs';
|
import { combineLatest } from 'rxjs';
|
||||||
import { map, tap } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
import { AsyncPipe, NgIf, NgStyle } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
interface ListItem extends IListable {
|
interface ListItem extends IListable {
|
||||||
readonly key: string;
|
readonly key: string;
|
||||||
@ -33,7 +21,6 @@ interface ListItem extends IListable {
|
|||||||
styleUrls: ['./default-colors-screen.component.scss'],
|
styleUrls: ['./default-colors-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
providers: listingProvidersFactory(DefaultColorsScreenComponent),
|
providers: listingProvidersFactory(DefaultColorsScreenComponent),
|
||||||
imports: [IqserListingModule, NgStyle, CircleButtonComponent, IqserAllowDirective, TranslateModule, AsyncPipe, NgIf],
|
|
||||||
})
|
})
|
||||||
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit {
|
export class DefaultColorsScreenComponent extends ListingComponent<ListItem> implements OnInit {
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
></redaction-kms-signature-configuration>
|
></redaction-kms-signature-configuration>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div [class.hidden]="!digitalSignature || !showChangesBox()" class="changes-box z-index-1000">
|
<div [class.hidden]="!digitalSignature" class="changes-box">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="saveDigitalSignature()"
|
(action)="saveDigitalSignature()"
|
||||||
[disabled]="disabled"
|
[disabled]="disabled"
|
||||||
|
|||||||
@ -20,7 +20,3 @@ form {
|
|||||||
input[name='keySecret']:not(:placeholder-shown) {
|
input[name='keySecret']:not(:placeholder-shown) {
|
||||||
font-family: 'secret', serif;
|
font-family: 'secret', serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.z-index-1000 {
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
|
|||||||
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