Compare commits
75 Commits
ng-updates
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f182be3db0 | ||
|
|
81a32f6d58 | ||
|
|
82552b1748 | ||
|
|
af7a45d739 | ||
|
|
21013a6fe5 | ||
|
|
c81ad67844 | ||
|
|
13797f1fb3 | ||
|
|
ef5cd39b16 | ||
|
|
e133e944e3 | ||
|
|
2ffb5bbb63 | ||
|
|
0f16644944 | ||
|
|
0b41159ee4 | ||
|
|
2a78aea898 | ||
|
|
4c5face779 | ||
|
|
b3a8a8d30c | ||
|
|
7836750171 | ||
|
|
ef9d3d2e8f | ||
|
|
3ca846b2c2 | ||
|
|
ae7b68bc16 | ||
|
|
3150134e65 | ||
|
|
b552628b43 | ||
|
|
9b3eb4702b | ||
|
|
52fa98f918 | ||
|
|
1d874f37b2 | ||
|
|
582ba6023e | ||
|
|
fbcb522b43 | ||
|
|
1d3096d82c | ||
|
|
43ea2e7857 | ||
|
|
92038a1949 | ||
|
|
a323ddd5dd | ||
|
|
e70cdd4cc5 | ||
|
|
427a81cff6 | ||
|
|
4933c7a678 | ||
|
|
8ee2ec5749 | ||
|
|
ce4216e56c | ||
|
|
a3526d338f | ||
|
|
65041f77c3 | ||
|
|
4206993c72 | ||
|
|
a7d8dc6a66 | ||
|
|
56bdf1bc4d | ||
|
|
acb009a909 | ||
|
|
96532fa61e | ||
|
|
eed7f1917e | ||
|
|
51b99089e4 | ||
|
|
118e35e897 | ||
|
|
b9197b1eb3 | ||
|
|
568bb696a7 | ||
|
|
bfe409305c | ||
|
|
00decf1f6c | ||
|
|
306b524dee | ||
|
|
ff8009167b | ||
|
|
b3dc8b04c8 | ||
|
|
c677bc17e4 | ||
|
|
8f541081ae | ||
|
|
9dd6085bcf | ||
|
|
eb9bd777e0 | ||
|
|
e3ef820bac | ||
|
|
9317f55d81 | ||
|
|
5d7849be45 | ||
|
|
74b4c1a11f | ||
|
|
0f79f04f52 | ||
|
|
6282675682 | ||
|
|
747d8157b5 | ||
|
|
c4c549fe1b | ||
|
|
e41ac70dfe | ||
|
|
59ce4177d2 | ||
|
|
0af6e45868 | ||
|
|
1bbef9eb37 | ||
|
|
35342707f0 | ||
|
|
c1fc52223b | ||
|
|
30c1f8628b | ||
|
|
66df5f807c | ||
|
|
f25d134590 | ||
|
|
10fa81e77c | ||
|
|
613a7429b8 |
@ -4,41 +4,60 @@ 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
|
- 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}
|
||||||
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
# git push https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.knecon.com/redactmanager/red-ui.git
|
||||||
# git push
|
# git push
|
||||||
fi
|
fi
|
||||||
rules:
|
rules:
|
||||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||||
|
|||||||
@ -135,20 +135,20 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
features: {
|
features: {
|
||||||
ANNOTATIONS: {
|
ANNOTATIONS: {
|
||||||
color: 'aqua',
|
color: 'aqua',
|
||||||
enabled: true,
|
enabled: false,
|
||||||
level: NgxLoggerLevel.DEBUG,
|
level: NgxLoggerLevel.DEBUG,
|
||||||
},
|
},
|
||||||
FILTERS: {
|
FILTERS: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
TENANTS: {
|
TENANTS: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
ROUTES: {
|
ROUTES: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
PDF: {
|
PDF: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
FILE: {
|
FILE: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@ -171,6 +171,9 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
DOSSIERS_CHANGES: {
|
DOSSIERS_CHANGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
|
GUARDS: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as ILoggerConfig,
|
} as ILoggerConfig,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -40,8 +40,7 @@ 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!');
|
logger.info('[KEYCLOAK] Keycloak init done for tenant: ', { tenant });
|
||||||
console.log({ tenant });
|
|
||||||
await tenantsService.selectTenant(tenant);
|
await tenantsService.selectTenant(tenant);
|
||||||
await usersService.initialize();
|
await usersService.initialize();
|
||||||
await licenseService.loadLicenses();
|
await licenseService.loadLicenses();
|
||||||
@ -51,6 +50,7 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,3 +69,5 @@ export const canEditHint = (annotation: AnnotationWrapper) =>
|
|||||||
((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage;
|
((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;
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
canRemoveRedaction,
|
canRemoveRedaction,
|
||||||
canResizeAnnotation,
|
canResizeAnnotation,
|
||||||
canResizeInDictionary,
|
canResizeInDictionary,
|
||||||
|
canRevertChanges,
|
||||||
canUndo,
|
canUndo,
|
||||||
} from './annotation-permissions.utils';
|
} from './annotation-permissions.utils';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
@ -37,6 +38,7 @@ export class AnnotationPermissions {
|
|||||||
canEditAnnotations = true;
|
canEditAnnotations = true;
|
||||||
canEditHints = true;
|
canEditHints = true;
|
||||||
canEditImages = true;
|
canEditImages = true;
|
||||||
|
canRevertChanges = true;
|
||||||
|
|
||||||
static forUser(
|
static forUser(
|
||||||
isApprover: boolean,
|
isApprover: boolean,
|
||||||
@ -75,6 +77,7 @@ export class AnnotationPermissions {
|
|||||||
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;
|
||||||
@ -97,6 +100,7 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ViewContainerRef } from '@angular/core';
|
||||||
import { Router, RouterOutlet } from '@angular/router';
|
import { Router, RouterOutlet } from '@angular/router';
|
||||||
import { accountTranslations } from '@translations/account-translations';
|
import { accountTranslations } from '@translations/account-translations';
|
||||||
import { NgClass, NgIf } from '@angular/common';
|
import { NgClass } from '@angular/common';
|
||||||
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
import { AccountSideNavComponent } from '../account-side-nav/account-side-nav.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
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, NgIf, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
imports: [NgClass, RouterOutlet, AccountSideNavComponent, TranslateModule],
|
||||||
})
|
})
|
||||||
export class BaseAccountScreenComponent implements OnInit {
|
export class BaseAccountScreenComponent implements OnInit {
|
||||||
readonly translations = accountTranslations;
|
readonly translations = accountTranslations;
|
||||||
|
|||||||
@ -17,7 +17,6 @@ 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 { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
@ -39,11 +38,9 @@ interface UserProfileForm {
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
NgIf,
|
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
NgForOf,
|
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
MatSlideToggle,
|
MatSlideToggle,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
|
|||||||
@ -1,22 +1,24 @@
|
|||||||
import { CompositeRouteGuard, IqserPermissionsGuard, IqserRoutes } from '@iqser/common-ui';
|
import { inject, provideEnvironmentInitializer } from '@angular/core';
|
||||||
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 { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
import { templateExistsWhenEnteringAdmin } from '@guards/dossier-template-exists.guard';
|
||||||
import { UserListingScreenComponent } from './screens/user-listing/user-listing-screen.component';
|
import { DossierTemplatesGuard } from '@guards/dossier-templates.guard';
|
||||||
import { DigitalSignatureScreenComponent } from './screens/digital-signature/digital-signature-screen.component';
|
import { entityExistsGuard } from '@guards/entity-exists-guard.service';
|
||||||
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
import { PermissionsGuard } from '@guards/permissions-guard';
|
||||||
import { GeneralConfigScreenComponent } from './screens/general-config/general-config-screen.component';
|
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 { BaseAdminScreenComponent } from './base-admin-screen/base-admin-screen.component';
|
||||||
import { BaseDossierTemplateScreenComponent } from './base-dossier-templates-screen/base-dossier-template-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 { BaseEntityScreenComponent } from './base-entity-screen/base-entity-screen.component';
|
||||||
import { PermissionsGuard } from '@guards/permissions-guard';
|
import { AuditScreenComponent } from './screens/audit/audit-screen.component';
|
||||||
import { Roles } from '@users/roles';
|
import { DefaultColorsScreenComponent } from './screens/default-colors/default-colors-screen.component';
|
||||||
import { IqserAuthGuard } from '@iqser/common-ui/lib/users';
|
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 { AdminDialogService } from './services/admin-dialog.service';
|
||||||
import { AuditService } from './services/audit.service';
|
import { AuditService } from './services/audit.service';
|
||||||
import { DigitalSignatureService } from './services/digital-signature.service';
|
import { DigitalSignatureService } from './services/digital-signature.service';
|
||||||
@ -78,7 +80,12 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
|||||||
},
|
},
|
||||||
type: 'ENTITY',
|
type: 'ENTITY',
|
||||||
},
|
},
|
||||||
providers: [RulesService],
|
providers: [
|
||||||
|
RulesService,
|
||||||
|
provideEnvironmentInitializer(() => {
|
||||||
|
return inject(CopilotService).connectAsync('/api/llm/llm-websocket');
|
||||||
|
}),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'component-rules',
|
path: 'component-rules',
|
||||||
|
|||||||
@ -1,12 +1,18 @@
|
|||||||
import { Component, OnInit, signal } from '@angular/core';
|
import { Component, OnInit, signal } from '@angular/core';
|
||||||
import { BaseFormComponent, CircleButtonComponent, IconButtonComponent, listingProvidersFactory, LoadingService } from '@iqser/common-ui';
|
import {
|
||||||
|
BaseFormComponent,
|
||||||
|
CircleButtonComponent,
|
||||||
|
HasScrollbarDirective,
|
||||||
|
IconButtonComponent,
|
||||||
|
listingProvidersFactory,
|
||||||
|
LoadingService,
|
||||||
|
} from '@iqser/common-ui';
|
||||||
import { ComponentDefinitionsService } from '@services/entity-services/component-definitions.service';
|
import { ComponentDefinitionsService } from '@services/entity-services/component-definitions.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { getParam } from '@common-ui/utils';
|
import { getParam } from '@common-ui/utils';
|
||||||
import { DOSSIER_TEMPLATE_ID, IComponentDefinition } from '@red/domain';
|
import { DOSSIER_TEMPLATE_ID, IComponentDefinition } from '@red/domain';
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
import { CommonModule } from '@angular/common';
|
||||||
import { CommonModule, NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
@ -20,8 +26,6 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
|
|||||||
providers: listingProvidersFactory(ComponentDefinitionsComponent),
|
providers: listingProvidersFactory(ComponentDefinitionsComponent),
|
||||||
imports: [
|
imports: [
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
InputWithActionComponent,
|
|
||||||
NgIf,
|
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
CommonModule,
|
CommonModule,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
@ -31,6 +35,7 @@ import { AdminDialogService } from '../../services/admin-dialog.service';
|
|||||||
FormsModule,
|
FormsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
|
HasScrollbarDirective,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export default class ComponentDefinitionsComponent extends BaseFormComponent implements OnInit {
|
export default class ComponentDefinitionsComponent extends BaseFormComponent implements OnInit {
|
||||||
@ -102,6 +107,7 @@ export default class ComponentDefinitionsComponent extends BaseFormComponent imp
|
|||||||
this._dialogService.openDialog('confirm', null, async () => {
|
this._dialogService.openDialog('confirm', null, async () => {
|
||||||
await firstValueFrom(this._componentDefinitionsService.deleteComponentDefinitions(this.#dossierTemplateId, [componentId]));
|
await firstValueFrom(this._componentDefinitionsService.deleteComponentDefinitions(this.#dossierTemplateId, [componentId]));
|
||||||
await this.#loadData();
|
await this.#loadData();
|
||||||
|
this.selectedComponent = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { NgForOf, NgIf } from '@angular/common';
|
|||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatDialogModule } from '@angular/material/dialog';
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
import { MatOption } from '@angular/material/autocomplete';
|
import { MatOption } from '@angular/material/autocomplete';
|
||||||
import { MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatSelect } from '@angular/material/select';
|
||||||
import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations';
|
import { fileAttributeEncodingTypesTranslations } from '@translations/file-attribute-encoding-types-translations';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { ComponentMappingsService } from '@services/entity-services/component-mappings.service';
|
import { ComponentMappingsService } from '@services/entity-services/component-mappings.service';
|
||||||
@ -40,7 +40,6 @@ interface DialogResult {
|
|||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
MatDialogModule,
|
MatDialogModule,
|
||||||
MatOption,
|
MatOption,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
UploadFileComponent,
|
UploadFileComponent,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { AsyncPipe, NgIf } from '@angular/common';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { RouterLink } from '@angular/router';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
||||||
import { getCurrentUser } from '@common-ui/users';
|
import { getCurrentUser } from '@common-ui/users';
|
||||||
@ -25,9 +24,6 @@ import { Roles } from '@users/roles';
|
|||||||
import { combineLatest, firstValueFrom } from 'rxjs';
|
import { combineLatest, firstValueFrom } 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 { AdminSideNavComponent } from '../../shared/components/admin-side-nav/admin-side-nav.component';
|
|
||||||
import { DossierTemplateActionsComponent } from '../../shared/components/dossier-template-actions/dossier-template-actions.component';
|
|
||||||
import { DossierTemplateBreadcrumbsComponent } from '../../shared/components/dossier-template-breadcrumbs/dossier-template-breadcrumbs.component';
|
|
||||||
import { AddEditComponentMappingDialogComponent } from './add-edit-component-mapping-dialog/add-edit-component-mapping-dialog.component';
|
import { AddEditComponentMappingDialogComponent } from './add-edit-component-mapping-dialog/add-edit-component-mapping-dialog.component';
|
||||||
import { download } from '@utils/file-download-utils';
|
import { download } from '@utils/file-download-utils';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
@ -37,14 +33,10 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||||||
styleUrls: ['./component-mappings-screen.component.scss'],
|
styleUrls: ['./component-mappings-screen.component.scss'],
|
||||||
providers: listingProvidersFactory(ComponentMappingsScreenComponent),
|
providers: listingProvidersFactory(ComponentMappingsScreenComponent),
|
||||||
imports: [
|
imports: [
|
||||||
DossierTemplateBreadcrumbsComponent,
|
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
NgIf,
|
NgIf,
|
||||||
DossierTemplateActionsComponent,
|
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
RouterLink,
|
|
||||||
AdminSideNavComponent,
|
|
||||||
IqserListingModule,
|
IqserListingModule,
|
||||||
InputWithActionComponent,
|
InputWithActionComponent,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { ArchivedDossiersService } from '@services/dossiers/archived-dossiers.se
|
|||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
|
|
||||||
export interface ConfirmDeleteDossierStateDialogData {
|
export interface ConfirmDeleteDossierStateDialogData {
|
||||||
@ -28,7 +28,6 @@ export interface ConfirmDeleteDossierStateDialogData {
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
MatCheckbox,
|
MatCheckbox,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { ChangeDetectionStrategy, Component, HostListener, ViewChild } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, HostListener, ViewChild } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { getConfig, HasScrollbarDirective, HelpButtonComponent, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
import { getConfig, HasScrollbarDirective, IconButtonComponent, IconButtonTypes } from '@iqser/common-ui';
|
||||||
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
||||||
import { Dictionary, DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
import { Dictionary, DOSSIER_TEMPLATE_ID, ENTITY_TYPE } from '@red/domain';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
@ -17,16 +17,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||||||
templateUrl: './entity-info.component.html',
|
templateUrl: './entity-info.component.html',
|
||||||
styleUrls: ['./entity-info.component.scss'],
|
styleUrls: ['./entity-info.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [
|
imports: [HasScrollbarDirective, MatIcon, NgIf, TranslateModule, AsyncPipe, IconButtonComponent, AddEditEntityComponent],
|
||||||
HasScrollbarDirective,
|
|
||||||
MatIcon,
|
|
||||||
NgIf,
|
|
||||||
TranslateModule,
|
|
||||||
AsyncPipe,
|
|
||||||
IconButtonComponent,
|
|
||||||
AddEditEntityComponent,
|
|
||||||
HelpButtonComponent,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class EntityInfoComponent {
|
export class EntityInfoComponent {
|
||||||
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
@ViewChild(AddEditEntityComponent) private readonly _addEditEntityComponent: AddEditEntityComponent;
|
||||||
|
|||||||
@ -57,6 +57,7 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
|
|||||||
if (supportCsvMapping) {
|
if (supportCsvMapping) {
|
||||||
return {
|
return {
|
||||||
...this.#configuration,
|
...this.#configuration,
|
||||||
|
keyColumn: this.form.get('keyColumn').value,
|
||||||
filenameMappingColumnHeaderName: this.form.get('keyColumn').value,
|
filenameMappingColumnHeaderName: this.form.get('keyColumn').value,
|
||||||
delimiter: this.form.get('delimiter').value,
|
delimiter: this.form.get('delimiter').value,
|
||||||
encoding: this.form.get('encodingType').value,
|
encoding: this.form.get('encodingType').value,
|
||||||
@ -66,13 +67,14 @@ export class FileAttributesConfigurationsDialogComponent extends BaseDialogCompo
|
|||||||
return {
|
return {
|
||||||
...this.#configuration,
|
...this.#configuration,
|
||||||
filenameMappingColumnHeaderName: '',
|
filenameMappingColumnHeaderName: '',
|
||||||
|
keyColumn: this.form.get('keyColumn').value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#getForm() {
|
#getForm() {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName],
|
supportCsvMapping: [!!this.#configuration.filenameMappingColumnHeaderName],
|
||||||
keyColumn: [this.#configuration.filenameMappingColumnHeaderName || '', [Validators.required]],
|
keyColumn: [this.#configuration.filenameMappingColumnHeaderName || this.#configuration.keyColumn || '', [Validators.required]],
|
||||||
delimiter: [this.#configuration.delimiter || '', [Validators.required]],
|
delimiter: [this.#configuration.delimiter || '', [Validators.required]],
|
||||||
encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]],
|
encodingType: [this.#configuration.encoding || FileAttributeEncodingTypes['UTF-8'], [Validators.required]],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
|||||||
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatMenu, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
import { MatSlideToggle } from '@angular/material/slide-toggle';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button';
|
import { ChevronButtonComponent } from '@common-ui/buttons/chevron-button';
|
||||||
@ -29,7 +29,6 @@ import { fileAttributeTypesTranslations } from '@translations/file-attribute-typ
|
|||||||
MatMenu,
|
MatMenu,
|
||||||
EditableInputComponent,
|
EditableInputComponent,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
@ -37,6 +36,7 @@ import { fileAttributeTypesTranslations } from '@translations/file-attribute-typ
|
|||||||
RoundCheckboxComponent,
|
RoundCheckboxComponent,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
NgIf,
|
NgIf,
|
||||||
|
MatMenuItem,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ActiveFieldsListingComponent extends ListingComponent<IField> implements OnChanges {
|
export class ActiveFieldsListingComponent extends ListingComponent<IField> implements OnChanges {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { AbstractControl, ReactiveFormsModule, UntypedFormBuilder, UntypedFormGr
|
|||||||
import { MatAutocomplete, MatAutocompleteTrigger, MatOption } from '@angular/material/autocomplete';
|
import { MatAutocomplete, MatAutocompleteTrigger, MatOption } from '@angular/material/autocomplete';
|
||||||
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogClose, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatSelect } from '@angular/material/select';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
||||||
import {
|
import {
|
||||||
@ -45,7 +45,6 @@ export interface IFileAttributesCSVImportData {
|
|||||||
MatAutocomplete,
|
MatAutocomplete,
|
||||||
AsyncPipe,
|
AsyncPipe,
|
||||||
MatOption,
|
MatOption,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
NgIf,
|
NgIf,
|
||||||
|
|||||||
@ -85,6 +85,7 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
|
keyColumnValue: string = '';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly permissionsService: PermissionsService,
|
readonly permissionsService: PermissionsService,
|
||||||
@ -171,13 +172,13 @@ export default class FileAttributesListingScreenComponent extends ListingCompone
|
|||||||
FileAttributesConfigurationsDialogComponent,
|
FileAttributesConfigurationsDialogComponent,
|
||||||
{
|
{
|
||||||
...defaultDialogConfig,
|
...defaultDialogConfig,
|
||||||
data: this.#existingConfiguration,
|
data: { ...this.#existingConfiguration, keyColumn: this.keyColumnValue },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const configuration = await firstValueFrom(ref.afterClosed());
|
const configuration = await firstValueFrom(ref.afterClosed());
|
||||||
|
|
||||||
if (configuration) {
|
if (configuration) {
|
||||||
|
this.keyColumnValue = configuration.keyColumn;
|
||||||
await this.#setConfigAndLoadData(configuration);
|
await this.#setConfigAndLoadData(configuration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,6 @@ import { Observable } from 'rxjs';
|
|||||||
import { DossierTemplateDetailsComponent } from '../dossier-template-details/dossier-template-details.component';
|
import { DossierTemplateDetailsComponent } from '../dossier-template-details/dossier-template-details.component';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
import { MatCheckbox } from '@angular/material/checkbox';
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { SelectComponent } from '@shared/components/select/select.component';
|
import { SelectComponent } from '@shared/components/select/select.component';
|
||||||
@ -42,7 +41,6 @@ const downloadTypes = ['ORIGINAL', 'PREVIEW', 'OPTIMIZED_PREVIEW', 'DELTA_PREVIE
|
|||||||
DossierTemplateDetailsComponent,
|
DossierTemplateDetailsComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
MatCheckbox,
|
MatCheckbox,
|
||||||
NgIf,
|
|
||||||
MatDatepickerModule,
|
MatDatepickerModule,
|
||||||
SelectComponent,
|
SelectComponent,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
|
|||||||
@ -1,8 +1,57 @@
|
|||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div [translate]="translations[this.type]['title']" class="heading-l"></div>
|
<div [translate]="translations[type()]['title']" class="heading-l"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
|
<div class="flex" style="height: 100%">
|
||||||
|
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
|
||||||
|
|
||||||
|
<div [class.collapsed]="collapsed()" class="right-container flex-column">
|
||||||
|
<div class="collapsed-wrapper">
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="collapsible; context: { action: 'expand', tooltip: ('copilot.label' | translate) }"
|
||||||
|
></ng-container>
|
||||||
|
<div class="all-caps-label" translate="copilot.label"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="header-wrapper flex mt-8">
|
||||||
|
<div class="heading-xl flex-1">{{ 'copilot.label' | translate | titlecase }}</div>
|
||||||
|
<ng-container
|
||||||
|
*ngTemplateOutlet="collapsible; context: { action: 'collapse', tooltip: ('copilot.label' | translate) }"
|
||||||
|
></ng-container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-24">
|
||||||
|
@for (comment of conversation(); track comment) {
|
||||||
|
<div class="comment">
|
||||||
|
<div class="comment-details-wrapper">
|
||||||
|
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label">
|
||||||
|
{{ comment.date | date: 'sophisticatedDate' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre class="text-auto">{{ comment.text }}</pre>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<iqser-input-with-action
|
||||||
|
(action)="add($event)"
|
||||||
|
[placeholder]="'comments.add-comment' | translate"
|
||||||
|
autocomplete="off"
|
||||||
|
icon="iqser:collapse"
|
||||||
|
width="full"
|
||||||
|
></iqser-input-with-action>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-template #collapsible let-action="action" let-tooltip="tooltip">
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="toggleCollapse()"
|
||||||
|
[icon]="'iqser:' + action"
|
||||||
|
[tooltipPosition]="IqserTooltipPositions.before"
|
||||||
|
[tooltip]="tooltip"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box">
|
<div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box">
|
||||||
<div (click)="goToErrors()" *ngIf="numberOfErrors() || numberOfWarnings()" class="errors">
|
<div (click)="goToErrors()" *ngIf="numberOfErrors() || numberOfWarnings()" class="errors">
|
||||||
@ -12,15 +61,15 @@
|
|||||||
<span
|
<span
|
||||||
*ngIf="numberOfErrors()"
|
*ngIf="numberOfErrors()"
|
||||||
[translateParams]="{ errors: numberOfErrors() }"
|
[translateParams]="{ errors: numberOfErrors() }"
|
||||||
[translate]="translations[this.type]['errors-found']"
|
[translate]="translations[type()]['errors-found']"
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
*ngIf="numberOfWarnings()"
|
*ngIf="numberOfWarnings()"
|
||||||
[translateParams]="{ warnings: numberOfWarnings() }"
|
|
||||||
[translate]="translations[this.type]['warnings-found']"
|
|
||||||
class="warning"
|
|
||||||
[class.only-warning]="!numberOfErrors()"
|
[class.only-warning]="!numberOfErrors()"
|
||||||
|
[translateParams]="{ warnings: numberOfWarnings() }"
|
||||||
|
[translate]="translations[type()]['warnings-found']"
|
||||||
|
class="warning"
|
||||||
></span>
|
></span>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
@ -29,11 +78,11 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<iqser-icon-button
|
<iqser-icon-button
|
||||||
(action)="save()"
|
(action)="save()"
|
||||||
[label]="translations[this.type]['save-changes'] | translate"
|
[label]="translations[type()]['save-changes'] | translate"
|
||||||
[type]="iconButtonTypes.primary"
|
[type]="iconButtonTypes.primary"
|
||||||
icon="iqser:check"
|
icon="iqser:check"
|
||||||
></iqser-icon-button>
|
></iqser-icon-button>
|
||||||
|
|
||||||
<div (click)="revert()" [translate]="translations[this.type]['revert-changes']" class="all-caps-label cancel"></div>
|
<div (click)="revert()" [translate]="translations[type()]['revert-changes']" class="all-caps-label cancel"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -81,3 +81,22 @@ ngx-monaco-editor {
|
|||||||
gap: 24px;
|
gap: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.right-container {
|
||||||
|
display: flex;
|
||||||
|
width: 750px;
|
||||||
|
min-width: 375px;
|
||||||
|
padding: 16px 24px 16px 24px;
|
||||||
|
|
||||||
|
&.has-scrollbar:hover {
|
||||||
|
padding-right: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
redaction-dossier-details {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-auto {
|
||||||
|
text-wrap: auto;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,22 +1,40 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, OnInit, signal } from '@angular/core';
|
import { NgIf, NgTemplateOutlet, TitleCasePipe } from '@angular/common';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import {
|
||||||
import { IconButtonComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
ChangeDetectionStrategy,
|
||||||
import { RulesService } from '../../../services/rules.service';
|
ChangeDetectorRef,
|
||||||
import { firstValueFrom } from 'rxjs';
|
Component,
|
||||||
import { DOSSIER_TEMPLATE_ID, DroolsKeywords, IRules } from '@red/domain';
|
computed,
|
||||||
import { EditorThemeService } from '@services/editor-theme.service';
|
DestroyRef,
|
||||||
|
inject,
|
||||||
|
input,
|
||||||
|
OnInit,
|
||||||
|
signal,
|
||||||
|
viewChild,
|
||||||
|
} from '@angular/core';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
|
import { InputWithActionComponent } from '@common-ui/inputs/input-with-action/input-with-action.component';
|
||||||
|
import { TenantsService } from '@common-ui/tenants';
|
||||||
|
import { getCurrentUser } from '@common-ui/users';
|
||||||
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
||||||
import { Debounce, getParam } from '@iqser/common-ui/lib/utils';
|
import { CircleButtonComponent, IconButtonComponent, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { Debounce, IqserTooltipPositions } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { DroolsKeywords, IRules } from '@red/domain';
|
||||||
|
import { CopilotService } from '@services/copilot.service';
|
||||||
|
import { EditorThemeService } from '@services/editor-theme.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { RulesService } from '../../../services/rules.service';
|
||||||
import { rulesScreenTranslations } from '../../../translations/rules-screen-translations';
|
import { rulesScreenTranslations } from '../../../translations/rules-screen-translations';
|
||||||
import ICodeEditor = monaco.editor.ICodeEditor;
|
import ICodeEditor = monaco.editor.ICodeEditor;
|
||||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||||
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
|
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
|
||||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
|
||||||
import { MatIcon } from '@angular/material/icon';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
interface SyntaxError {
|
interface SyntaxError {
|
||||||
line: number;
|
line: number;
|
||||||
@ -31,18 +49,57 @@ interface UploadResponse {
|
|||||||
deprecatedWarnings: SyntaxError[];
|
deprecatedWarnings: SyntaxError[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SentenceTypes = {
|
||||||
|
question: 'question',
|
||||||
|
answer: 'answer',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type SentenceType = keyof typeof SentenceTypes;
|
||||||
|
|
||||||
|
interface Sentence {
|
||||||
|
text: string | null;
|
||||||
|
date: string;
|
||||||
|
type: SentenceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const endingSentence: Sentence = { text: null, date: new Date().toISOString(), type: SentenceTypes.answer };
|
||||||
|
|
||||||
const RULE_VALIDATION_TIMEOUT = 2000;
|
const RULE_VALIDATION_TIMEOUT = 2000;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './rules-screen.component.html',
|
templateUrl: './rules-screen.component.html',
|
||||||
styleUrls: ['./rules-screen.component.scss'],
|
styleUrls: ['./rules-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
imports: [MonacoEditorModule, MatIcon, FormsModule, IconButtonComponent, NgIf, TranslateModule],
|
imports: [
|
||||||
|
MonacoEditorModule,
|
||||||
|
MatIcon,
|
||||||
|
FormsModule,
|
||||||
|
IconButtonComponent,
|
||||||
|
NgIf,
|
||||||
|
TranslateModule,
|
||||||
|
CircleButtonComponent,
|
||||||
|
InputWithActionComponent,
|
||||||
|
MatTooltip,
|
||||||
|
NgTemplateOutlet,
|
||||||
|
DatePipe,
|
||||||
|
TitleCasePipe,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
export default class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
export default class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||||
|
readonly #errorGlyphs = signal<string[]>([]);
|
||||||
|
#codeEditor: ICodeEditor;
|
||||||
|
#decorations: string[] = [];
|
||||||
|
readonly #errors = signal<SyntaxError[]>([]);
|
||||||
|
#ruleValidationTimeout: number = null;
|
||||||
|
readonly #copilotService = inject(CopilotService);
|
||||||
|
readonly #currentUser = getCurrentUser();
|
||||||
|
readonly #conversation = signal<Sentence[]>([endingSentence]);
|
||||||
|
protected readonly collapsed = signal(true);
|
||||||
|
protected readonly IqserTooltipPositions = IqserTooltipPositions;
|
||||||
|
readonly dossierTemplateId = input.required<string>();
|
||||||
readonly translations = rulesScreenTranslations;
|
readonly translations = rulesScreenTranslations;
|
||||||
|
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
|
readonly inputWithAction = viewChild(InputWithActionComponent);
|
||||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||||
theme: 'vs',
|
theme: 'vs',
|
||||||
language: 'java',
|
language: 'java',
|
||||||
@ -54,19 +111,43 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
initialLines: string[] = [];
|
initialLines: string[] = [];
|
||||||
currentLines: string[] = [];
|
currentLines: string[] = [];
|
||||||
isLeaving = false;
|
isLeaving = false;
|
||||||
readonly type: IRules['ruleFileType'];
|
readonly type = input.required<IRules['ruleFileType']>();
|
||||||
readonly #errorGlyphs = signal<string[]>([]);
|
|
||||||
readonly numberOfErrors = computed(() => this.#errors().filter(e => !e.warning).length);
|
readonly numberOfErrors = computed(() => this.#errors().filter(e => !e.warning).length);
|
||||||
readonly numberOfWarnings = computed(() => this.#errors().filter(e => e.warning).length);
|
readonly numberOfWarnings = computed(() => this.#errors().filter(e => e.warning).length);
|
||||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
readonly conversation = computed(() => this.#conversation().filter(r => !!r.text));
|
||||||
#codeEditor: ICodeEditor;
|
|
||||||
#decorations: string[] = [];
|
constructor(
|
||||||
#errors = signal<SyntaxError[]>([]);
|
readonly permissionsService: PermissionsService,
|
||||||
#ruleValidationTimeout: number = null;
|
private readonly _rulesService: RulesService,
|
||||||
|
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private readonly _toaster: Toaster,
|
||||||
|
private readonly _loadingService: LoadingService,
|
||||||
|
private readonly _editorThemeService: EditorThemeService,
|
||||||
|
) {
|
||||||
|
const username = this.#currentUser.id;
|
||||||
|
const tenant = inject(TenantsService).activeTenantId;
|
||||||
|
inject(DestroyRef).onDestroy(() => this.#copilotService.deactivate());
|
||||||
|
this.#copilotService
|
||||||
|
.listen<{ token?: string }>('/user/' + username + '/queue/' + tenant + '/rules-copilot')
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(),
|
||||||
|
map(res => res?.token),
|
||||||
|
)
|
||||||
|
.subscribe(token => {
|
||||||
|
if (token === null) {
|
||||||
|
this.#conversation.update(responses => [...responses, { ...endingSentence }]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#conversation.update(responses => {
|
||||||
|
const last = responses.pop();
|
||||||
|
return [...responses, { ...last, text: (last.text ?? '') + token }];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
set isLeavingPage(isLeaving: boolean) {
|
set isLeavingPage(isLeaving: boolean) {
|
||||||
this.isLeaving = isLeaving;
|
this.isLeaving = isLeaving;
|
||||||
this._changeDetectorRef.detectChanges();
|
this._changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
get changed(): boolean {
|
get changed(): boolean {
|
||||||
@ -84,16 +165,23 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
this.#closeProblemsView();
|
this.#closeProblemsView();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
toggleCollapse() {
|
||||||
readonly permissionsService: PermissionsService,
|
this.collapsed.update(collapsed => !collapsed);
|
||||||
private readonly _rulesService: RulesService,
|
if (this.#conversation().length === 1) {
|
||||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
this.#copilotService.send('Hello!');
|
||||||
private readonly _toaster: Toaster,
|
}
|
||||||
private readonly _loadingService: LoadingService,
|
}
|
||||||
private readonly _editorThemeService: EditorThemeService,
|
|
||||||
private readonly _route: ActivatedRoute,
|
add(question: string) {
|
||||||
) {
|
this.#conversation.update(responses => {
|
||||||
this.type = this._route.snapshot.data.type;
|
const last = responses.pop();
|
||||||
|
last.text = question;
|
||||||
|
last.type = SentenceTypes.question;
|
||||||
|
last.date = new Date().toISOString();
|
||||||
|
return [...responses, last, { ...endingSentence }];
|
||||||
|
});
|
||||||
|
this.inputWithAction().reset();
|
||||||
|
this.#copilotService.send(question);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@ -121,7 +209,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
}
|
}
|
||||||
(window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true));
|
(window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true));
|
||||||
await this.#configureSyntaxHighlighting();
|
await this.#configureSyntaxHighlighting();
|
||||||
this._changeDetectorRef.detectChanges();
|
this._changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Debounce()
|
@Debounce()
|
||||||
@ -141,12 +229,20 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
await this.#uploadRules();
|
await this.#uploadRules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revert(): void {
|
||||||
|
this.currentLines = this.initialLines;
|
||||||
|
this.#decorations = this.#codeEditor?.deltaDecorations(this.#decorations, []) || [];
|
||||||
|
this.#removeErrorMarkers();
|
||||||
|
this._changeDetectorRef.markForCheck();
|
||||||
|
this._loadingService.stop();
|
||||||
|
}
|
||||||
|
|
||||||
async #uploadRules(dryRun = false) {
|
async #uploadRules(dryRun = false) {
|
||||||
return firstValueFrom(
|
return firstValueFrom(
|
||||||
this._rulesService.uploadRules({
|
this._rulesService.uploadRules({
|
||||||
rules: this.#getValue(),
|
rules: this.#getValue(),
|
||||||
dossierTemplateId: this.#dossierTemplateId,
|
dossierTemplateId: this.dossierTemplateId(),
|
||||||
ruleFileType: this.type,
|
ruleFileType: this.type(),
|
||||||
dryRun,
|
dryRun,
|
||||||
}),
|
}),
|
||||||
).then(
|
).then(
|
||||||
@ -155,7 +251,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
this.#drawErrorMarkers(errors);
|
this.#drawErrorMarkers(errors);
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
await this.#initialize();
|
await this.#initialize();
|
||||||
this._toaster.success(rulesScreenTranslations[this.type]['success.generic']);
|
this._toaster.success(rulesScreenTranslations[this.type()]['success.generic']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
@ -172,20 +268,12 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
this.#drawErrorMarkers(errors);
|
this.#drawErrorMarkers(errors);
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
if (!dryRun) {
|
if (!dryRun) {
|
||||||
this._toaster.error(rulesScreenTranslations[this.type]['error.generic']);
|
this._toaster.error(rulesScreenTranslations[this.type()]['error.generic']);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
revert(): void {
|
|
||||||
this.currentLines = this.initialLines;
|
|
||||||
this.#decorations = this.#codeEditor?.deltaDecorations(this.#decorations, []) || [];
|
|
||||||
this.#removeErrorMarkers();
|
|
||||||
this._changeDetectorRef.detectChanges();
|
|
||||||
this._loadingService.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#mapErrors(response: UploadResponse, dryRun = false) {
|
#mapErrors(response: UploadResponse, dryRun = false) {
|
||||||
const warnings = response.deprecatedWarnings.map(w => ({ ...w, warning: true }));
|
const warnings = response.deprecatedWarnings.map(w => ({ ...w, warning: true }));
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
@ -295,7 +383,7 @@ export default class RulesScreenComponent implements OnInit, ComponentCanDeactiv
|
|||||||
|
|
||||||
async #initialize() {
|
async #initialize() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
await firstValueFrom(this._rulesService.download(this.#dossierTemplateId, this.type)).then(
|
await firstValueFrom(this._rulesService.download(this.dossierTemplateId(), this.type())).then(
|
||||||
rules => {
|
rules => {
|
||||||
this.currentLines = this.initialLines = rules.rules.split('\n');
|
this.currentLines = this.initialLines = rules.rules.split('\n');
|
||||||
this.revert();
|
this.revert();
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import { ConfigureCertificateDialogComponent } from '../dialogs/configure-digita
|
|||||||
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
|
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
|
||||||
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
|
||||||
import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
|
import { UploadDictionaryDialogComponent } from '../dialogs/upload-dictionary-dialog/upload-dictionary-dialog.component';
|
||||||
|
import { UserStatsService } from './user-stats.service';
|
||||||
|
import { result } from 'lodash-es';
|
||||||
|
|
||||||
type DialogType =
|
type DialogType =
|
||||||
| 'confirm'
|
| 'confirm'
|
||||||
@ -73,19 +75,26 @@ export class AdminDialogService extends DialogService<DialogType> {
|
|||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
private readonly _activeDossiersService: ActiveDossiersService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
|
private readonly _userStatsService: UserStatsService,
|
||||||
private readonly _reportTemplateService: ReportTemplateService,
|
private readonly _reportTemplateService: ReportTemplateService,
|
||||||
) {
|
) {
|
||||||
super(_dialog);
|
super(_dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteUsers(userIds: string[], cb?: () => Promise<void> | void): void {
|
async deleteUsers(userIds: string[], cb?: () => Promise<void> | void): Promise<void> {
|
||||||
|
const userStats = await firstValueFrom(this._userStatsService.getOne(userIds[0]));
|
||||||
|
|
||||||
const data: IConfirmationDialogData = {
|
const data: IConfirmationDialogData = {
|
||||||
title: _('confirm-delete-users.title'),
|
title: _('confirm-delete-users.title'),
|
||||||
question: _('confirm-delete-users.warning'),
|
question: _('confirm-delete-users.warning'),
|
||||||
confirmationText: _('confirm-delete-users.delete'),
|
confirmationText: _('confirm-delete-users.delete'),
|
||||||
denyText: _('confirm-delete-users.cancel'),
|
denyText: _('confirm-delete-users.cancel'),
|
||||||
titleColor: TitleColors.WARN,
|
titleColor: TitleColors.WARN,
|
||||||
translateParams: { usersCount: 1, dossiersCount: this._getUsersDossiersCount(userIds) },
|
translateParams: {
|
||||||
|
usersCount: 1,
|
||||||
|
dossiersCount: userStats.numberOfDossierOwnerships,
|
||||||
|
documentsCount: userStats.numberOfAssignedFiles,
|
||||||
|
},
|
||||||
checkboxes: [
|
checkboxes: [
|
||||||
{ value: false, label: _('confirm-delete-users.impacted-dossiers') },
|
{ value: false, label: _('confirm-delete-users.impacted-dossiers') },
|
||||||
{ value: false, label: _('confirm-delete-users.impacted-documents') },
|
{ value: false, label: _('confirm-delete-users.impacted-documents') },
|
||||||
|
|||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { StatsService } from '@iqser/common-ui';
|
||||||
|
import { IUserStats, USER_ID, UserStats } from '@red/domain';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class UserStatsService extends StatsService<UserStats, IUserStats> {
|
||||||
|
protected readonly _primaryKey = USER_ID;
|
||||||
|
protected readonly _entityClass = UserStats;
|
||||||
|
protected readonly _defaultModelPath = 'user-stats';
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { Component, Input, OnInit } from '@angular/core';
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { CircleButtonComponent, LoadingService } from '@iqser/common-ui';
|
import { CircleButtonComponent, LoadingService } from '@iqser/common-ui';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -13,7 +13,7 @@ import { AdminDialogService } from '../../../services/admin-dialog.service';
|
|||||||
selector: 'redaction-dossier-template-actions',
|
selector: 'redaction-dossier-template-actions',
|
||||||
templateUrl: './dossier-template-actions.component.html',
|
templateUrl: './dossier-template-actions.component.html',
|
||||||
styleUrls: ['./dossier-template-actions.component.scss'],
|
styleUrls: ['./dossier-template-actions.component.scss'],
|
||||||
imports: [NgIf, CircleButtonComponent, TranslateModule, RouterLink],
|
imports: [NgIf, CircleButtonComponent, TranslateModule],
|
||||||
})
|
})
|
||||||
export class DossierTemplateActionsComponent implements OnInit {
|
export class DossierTemplateActionsComponent implements OnInit {
|
||||||
@Input() dossierTemplateId: string;
|
@Input() dossierTemplateId: string;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AsyncPipe, NgClass, NgIf, NgTemplateOutlet } from '@angular/common';
|
import { AsyncPipe, NgClass, NgIf } from '@angular/common';
|
||||||
import { Component, computed, effect, HostListener, input, Input, OnDestroy } from '@angular/core';
|
import { Component, computed, effect, HostListener, input, Input, OnDestroy } from '@angular/core';
|
||||||
import { AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
import { AbstractControl, FormBuilder, FormsModule, ReactiveFormsModule, UntypedFormGroup, ValidatorFn } from '@angular/forms';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
@ -41,7 +41,6 @@ import { ConfigService } from '../../config.service';
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
DynamicInputComponent,
|
DynamicInputComponent,
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
NgTemplateOutlet,
|
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
StopPropagationDirective,
|
StopPropagationDirective,
|
||||||
],
|
],
|
||||||
@ -90,14 +89,11 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
);
|
);
|
||||||
this.#subscriptions.add(sub2.subscribe());
|
this.#subscriptions.add(sub2.subscribe());
|
||||||
|
|
||||||
effect(
|
effect(() => {
|
||||||
() => {
|
if (this.#shouldClose()) {
|
||||||
if (this.#shouldClose()) {
|
this.close();
|
||||||
this.close();
|
}
|
||||||
}
|
});
|
||||||
},
|
|
||||||
{ allowSignalWrites: true },
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isDate(): boolean {
|
get isDate(): boolean {
|
||||||
@ -131,7 +127,6 @@ export class FileAttributeComponent extends BaseFormComponent implements OnDestr
|
|||||||
|
|
||||||
handleClick($event: MouseEvent) {
|
handleClick($event: MouseEvent) {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
$event.preventDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
@use 'common-mixins';
|
@use 'common-mixins';
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: var(--iqser-primary);
|
color: #dd4d50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.extend-cols {
|
.extend-cols {
|
||||||
|
|||||||
@ -27,7 +27,9 @@
|
|||||||
<redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute>
|
<redaction-file-attribute [dossier]="dossier" [fileAttribute]="config" [file]="file"></redaction-file-attribute>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-file-workload [file]="file"></redaction-file-workload>
|
@if (!isDocumine) {
|
||||||
|
<redaction-file-workload [file]="file"></redaction-file-workload>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="file-actions overflow-visible">
|
<div class="file-actions overflow-visible">
|
||||||
<redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator>
|
<redaction-processing-indicator [file]="file" class="mr-8"></redaction-processing-indicator>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ChangeDetectorRef, Component, computed, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
|
import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, Optional, ViewChild } from '@angular/core';
|
||||||
import { DisableStopPropagationDirective, HelpModeService } from '@iqser/common-ui';
|
import { DisableStopPropagationDirective, getConfig, HelpModeService } from '@iqser/common-ui';
|
||||||
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
|
import { Debounce, trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||||
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
import { Dossier, File, IFileAttributeConfig } from '@red/domain';
|
||||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||||
@ -36,6 +36,7 @@ import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
|||||||
export class WorkflowItemComponent implements OnInit {
|
export class WorkflowItemComponent implements OnInit {
|
||||||
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
@ViewChild('actionsWrapper', { static: true }) private _actionsWrapper: ElementRef;
|
||||||
width: number;
|
width: number;
|
||||||
|
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly trackBy = trackByFactory();
|
readonly trackBy = trackByFactory();
|
||||||
@Input({ required: true }) file: File;
|
@Input({ required: true }) file: File;
|
||||||
@Input({ required: true }) dossier: Dossier;
|
@Input({ required: true }) dossier: Dossier;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { AsyncPipe, NgIf } from '@angular/common';
|
||||||
import { Component, ElementRef, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
import { Component, ElementRef, HostListener, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
@ -16,6 +17,7 @@ import {
|
|||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
import { NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
|
import { getParam, OnAttach, OnDetach, shareLast } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
Dossier,
|
Dossier,
|
||||||
DOSSIER_ID,
|
DOSSIER_ID,
|
||||||
@ -26,6 +28,7 @@ import {
|
|||||||
WorkflowFileStatus,
|
WorkflowFileStatus,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
|
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
|
import { DossierAttributesService } from '@services/entity-services/dossier-attributes.service';
|
||||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||||
@ -33,6 +36,7 @@ import { FileAttributesService } from '@services/entity-services/file-attributes
|
|||||||
import { FilesMapService } from '@services/files/files-map.service';
|
import { FilesMapService } from '@services/files/files-map.service';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component';
|
||||||
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
import { FileUploadModel } from '@upload-download/model/file-upload.model';
|
||||||
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service';
|
||||||
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
import { FileUploadService } from '@upload-download/services/file-upload.service';
|
||||||
@ -42,17 +46,13 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
|||||||
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
import { convertFiles, Files, handleFileDrop } from '@utils/index';
|
||||||
import { merge, Observable } from 'rxjs';
|
import { merge, Observable } from 'rxjs';
|
||||||
import { filter, map, skip, switchMap, tap } from 'rxjs/operators';
|
import { filter, map, skip, switchMap, tap } from 'rxjs/operators';
|
||||||
|
import { DossierOverviewBulkActionsComponent } from '../components/bulk-actions/dossier-overview-bulk-actions.component';
|
||||||
|
import { DossierDetailsComponent } from '../components/dossier-details/dossier-details.component';
|
||||||
|
import { DossierOverviewScreenHeaderComponent } from '../components/screen-header/dossier-overview-screen-header.component';
|
||||||
|
import { TableItemComponent } from '../components/table-item/table-item.component';
|
||||||
|
import { WorkflowItemComponent } from '../components/workflow-item/workflow-item.component';
|
||||||
import { ConfigService } from '../config.service';
|
import { ConfigService } from '../config.service';
|
||||||
import { BulkActionsService } from '../services/bulk-actions.service';
|
import { BulkActionsService } from '../services/bulk-actions.service';
|
||||||
import { DossiersCacheService } from '@services/dossiers/dossiers-cache.service';
|
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
|
||||||
import { DossierOverviewScreenHeaderComponent } from '../components/screen-header/dossier-overview-screen-header.component';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { WorkflowItemComponent } from '../components/workflow-item/workflow-item.component';
|
|
||||||
import { DossierDetailsComponent } from '../components/dossier-details/dossier-details.component';
|
|
||||||
import { DossierOverviewBulkActionsComponent } from '../components/bulk-actions/dossier-overview-bulk-actions.component';
|
|
||||||
import { TableItemComponent } from '../components/table-item/table-item.component';
|
|
||||||
import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './dossier-overview-screen.component.html',
|
templateUrl: './dossier-overview-screen.component.html',
|
||||||
|
|||||||
@ -83,14 +83,15 @@
|
|||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<ng-template [allow]="roles.redactions.deleteManual" [allowIf]="annotationPermissions().canUndo">
|
||||||
(action)="annotationActionsService.undoDirectAction(annotations())"
|
<iqser-circle-button
|
||||||
*allow="roles.redactions.deleteManual; if: annotationPermissions().canUndo"
|
(action)="annotationActionsService.undoDirectAction(annotations())"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="'annotation-actions.undo' | translate"
|
[tooltip]="'annotation-actions.undo' | translate"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
icon="red:undo"
|
icon="red:undo"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="annotationReferencesService.show(annotations()[0])"
|
(action)="annotationReferencesService.show(annotations()[0])"
|
||||||
@ -150,6 +151,15 @@
|
|||||||
icon="iqser:visibility"
|
icon="iqser:visibility"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="revertChanges()"
|
||||||
|
*ngIf="canRevertChanges() && devMode"
|
||||||
|
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-undo' : 'annotations-undo'"
|
||||||
|
[type]="buttonType"
|
||||||
|
[iconSize]="16"
|
||||||
|
icon="red:revert-changes"
|
||||||
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="removeRedaction()"
|
(action)="removeRedaction()"
|
||||||
*ngIf="canRemoveRedaction()"
|
*ngIf="canRemoveRedaction()"
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
import { AsyncPipe, NgIf } from '@angular/common';
|
||||||
import { Component, computed, input, Input, untracked } from '@angular/core';
|
import { Component, computed, input, Input, untracked } from '@angular/core';
|
||||||
import { CircleButtonComponent, getConfig, HelpModeService, IqserAllowDirective, IqserPermissionsService } from '@iqser/common-ui';
|
import { CircleButtonComponent, getConfig, HelpModeService, IqserAllowDirective, IqserPermissionsService } from '@iqser/common-ui';
|
||||||
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 { TranslateModule } from '@ngx-translate/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
||||||
@ -9,10 +11,9 @@ import { AnnotationActionsService } from '../../services/annotation-actions.serv
|
|||||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
import { ViewModeService } from '../../services/view-mode.service';
|
|
||||||
import { SkippedService } from '../../services/skipped.service';
|
import { SkippedService } from '../../services/skipped.service';
|
||||||
import { AsyncPipe, NgIf } from '@angular/common';
|
import { ViewModeService } from '../../services/view-mode.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
|
||||||
export const AnnotationButtonTypes = {
|
export const AnnotationButtonTypes = {
|
||||||
default: 'default',
|
default: 'default',
|
||||||
@ -28,12 +29,14 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
|
|||||||
imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective],
|
imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective],
|
||||||
})
|
})
|
||||||
export class AnnotationActionsComponent {
|
export class AnnotationActionsComponent {
|
||||||
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
|
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
|
||||||
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
||||||
@Input() canPerformAnnotationActions: boolean;
|
@Input() canPerformAnnotationActions: boolean;
|
||||||
@Input() alwaysVisible: boolean;
|
@Input() alwaysVisible: boolean;
|
||||||
@Input() actionsHelpModeKey: string;
|
@Input() actionsHelpModeKey: string;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
|
readonly devMode = this._userPreferences.isIqserDevMode;
|
||||||
readonly annotations = input.required<AnnotationWrapper[], (AnnotationWrapper | undefined)[]>({
|
readonly annotations = input.required<AnnotationWrapper[], (AnnotationWrapper | undefined)[]>({
|
||||||
transform: value => value.filter(a => a !== undefined),
|
transform: value => value.filter(a => a !== undefined),
|
||||||
});
|
});
|
||||||
@ -67,6 +70,10 @@ export class AnnotationActionsComponent {
|
|||||||
readonly hideSkipped = computed(() => this.skippedService.hideSkipped() && this.annotations().some(a => a.isSkipped));
|
readonly hideSkipped = computed(() => this.skippedService.hideSkipped() && this.annotations().some(a => a.isSkipped));
|
||||||
readonly isImageHint = computed(() => this.annotations().every(a => a.IMAGE_HINT));
|
readonly isImageHint = computed(() => this.annotations().every(a => a.IMAGE_HINT));
|
||||||
readonly isImage = computed(() => this.annotations().reduce((acc, a) => acc && a.isImage, true));
|
readonly isImage = computed(() => this.annotations().reduce((acc, a) => acc && a.isImage, true));
|
||||||
|
readonly canRevertChanges = computed(() => this.annotationPermissions().canRevertChanges);
|
||||||
|
readonly annotationChangesAllowed = computed(
|
||||||
|
() => (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.somePending(),
|
||||||
|
);
|
||||||
readonly canRemoveRedaction = computed(
|
readonly canRemoveRedaction = computed(
|
||||||
() => this.annotationChangesAllowed() && this.annotationPermissions().canRemoveRedaction && this.sameType(),
|
() => this.annotationChangesAllowed() && this.annotationPermissions().canRemoveRedaction && this.sameType(),
|
||||||
);
|
);
|
||||||
@ -75,10 +82,6 @@ export class AnnotationActionsComponent {
|
|||||||
readonly canAcceptRecommendation = computed(
|
readonly canAcceptRecommendation = computed(
|
||||||
() => this.annotationChangesAllowed() && this.annotationPermissions().canAcceptRecommendation,
|
() => this.annotationChangesAllowed() && this.annotationPermissions().canAcceptRecommendation,
|
||||||
);
|
);
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
readonly annotationChangesAllowed = computed(
|
|
||||||
() => (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.somePending(),
|
|
||||||
);
|
|
||||||
readonly canResize = computed(
|
readonly canResize = computed(
|
||||||
() => this.annotationChangesAllowed() && this.annotationPermissions().canResizeAnnotation && this.annotations().length === 1,
|
() => this.annotationChangesAllowed() && this.annotationPermissions().canResizeAnnotation && this.annotations().length === 1,
|
||||||
);
|
);
|
||||||
@ -104,13 +107,14 @@ export class AnnotationActionsComponent {
|
|||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
readonly helpModeService: HelpModeService,
|
readonly helpModeService: HelpModeService,
|
||||||
readonly multiSelectService: MultiSelectService,
|
readonly multiSelectService: MultiSelectService,
|
||||||
|
readonly skippedService: SkippedService,
|
||||||
|
readonly annotationActionsService: AnnotationActionsService,
|
||||||
|
readonly annotationReferencesService: AnnotationReferencesService,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
private readonly _iqserPermissionsService: IqserPermissionsService,
|
private readonly _iqserPermissionsService: IqserPermissionsService,
|
||||||
private readonly _annotationManager: REDAnnotationManager,
|
private readonly _annotationManager: REDAnnotationManager,
|
||||||
readonly skippedService: SkippedService,
|
private readonly _userPreferences: UserPreferenceService,
|
||||||
readonly annotationActionsService: AnnotationActionsService,
|
|
||||||
readonly annotationReferencesService: AnnotationReferencesService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
get resized(): boolean {
|
get resized(): boolean {
|
||||||
@ -128,6 +132,11 @@ export class AnnotationActionsComponent {
|
|||||||
await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept');
|
await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async revertChanges() {
|
||||||
|
const annotations = untracked(this.annotations);
|
||||||
|
await this.annotationActionsService.revertChanges(annotations);
|
||||||
|
}
|
||||||
|
|
||||||
hideAnnotation() {
|
hideAnnotation() {
|
||||||
const viewerAnnotations = untracked(this.viewerAnnotations);
|
const viewerAnnotations = untracked(this.viewerAnnotations);
|
||||||
this._annotationManager.hide(viewerAnnotations);
|
this._annotationManager.hide(viewerAnnotations);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
|||||||
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
|
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
import { LogEntryEngine, LogEntryEngines } from '@red/domain';
|
import { LogEntryEngine, LogEntryEngines } from '@red/domain';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf } from '@angular/common';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
||||||
@ -36,7 +36,7 @@ const changesProperties: KeysOf<AnnotationWrapper>[] = [
|
|||||||
selector: 'redaction-annotation-details',
|
selector: 'redaction-annotation-details',
|
||||||
templateUrl: './annotation-details.component.html',
|
templateUrl: './annotation-details.component.html',
|
||||||
styleUrls: ['./annotation-details.component.scss'],
|
styleUrls: ['./annotation-details.component.scss'],
|
||||||
imports: [NgIf, MatTooltip, MatIcon, CdkOverlayOrigin, NgForOf, CdkConnectedOverlay, TranslateModule],
|
imports: [MatTooltip, MatIcon, CdkOverlayOrigin, NgForOf, CdkConnectedOverlay, TranslateModule],
|
||||||
})
|
})
|
||||||
export class AnnotationDetailsComponent {
|
export class AnnotationDetailsComponent {
|
||||||
readonly annotation = input.required<ListItem<AnnotationWrapper>>();
|
readonly annotation = input.required<ListItem<AnnotationWrapper>>();
|
||||||
|
|||||||
@ -1,30 +1,30 @@
|
|||||||
|
import { Clipboard } from '@angular/cdk/clipboard';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
import { Component, computed, ElementRef, input, output } from '@angular/core';
|
import { Component, computed, ElementRef, input, output } from '@angular/core';
|
||||||
import { getConfig, HasScrollbarDirective } from '@iqser/common-ui';
|
import { getConfig, HasScrollbarDirective } from '@iqser/common-ui';
|
||||||
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
||||||
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
|
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { ListItem } from '@models/file/list-item';
|
import { ListItem } from '@models/file/list-item';
|
||||||
import { EarmarkGroup } from '@red/domain';
|
import { EarmarkGroup } from '@red/domain';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
import { isTargetInput } from '@utils/functions';
|
||||||
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
|
||||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||||
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
import { ViewModeService } from '../../services/view-mode.service';
|
import { ViewModeService } from '../../services/view-mode.service';
|
||||||
import { JsonPipe, NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { HighlightsSeparatorComponent } from '../highlights-separator/highlights-separator.component';
|
|
||||||
import { AnnotationWrapperComponent } from '../annotation-wrapper/annotation-wrapper.component';
|
|
||||||
import { AnnotationReferencesListComponent } from '../annotation-references-list/annotation-references-list.component';
|
import { AnnotationReferencesListComponent } from '../annotation-references-list/annotation-references-list.component';
|
||||||
import { Clipboard } from '@angular/cdk/clipboard';
|
import { AnnotationWrapperComponent } from '../annotation-wrapper/annotation-wrapper.component';
|
||||||
import { isTargetInput } from '@utils/functions';
|
import { HighlightsSeparatorComponent } from '../highlights-separator/highlights-separator.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-annotations-list',
|
selector: 'redaction-annotations-list',
|
||||||
templateUrl: './annotations-list.component.html',
|
templateUrl: './annotations-list.component.html',
|
||||||
styleUrls: ['./annotations-list.component.scss'],
|
styleUrls: ['./annotations-list.component.scss'],
|
||||||
imports: [NgForOf, NgIf, HighlightsSeparatorComponent, AnnotationWrapperComponent, AnnotationReferencesListComponent, JsonPipe],
|
imports: [NgIf, AnnotationReferencesListComponent, HighlightsSeparatorComponent, AnnotationWrapperComponent],
|
||||||
})
|
})
|
||||||
export class AnnotationsListComponent extends HasScrollbarDirective {
|
export class AnnotationsListComponent extends HasScrollbarDirective {
|
||||||
|
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly annotations = input.required<ListItem<AnnotationWrapper>[]>();
|
readonly annotations = input.required<ListItem<AnnotationWrapper>[]>();
|
||||||
readonly pagesPanelActive = output<boolean>();
|
readonly pagesPanelActive = output<boolean>();
|
||||||
readonly displayedHighlightGroups = computed(() => {
|
readonly displayedHighlightGroups = computed(() => {
|
||||||
@ -44,7 +44,6 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _elementRef: ElementRef,
|
protected readonly _elementRef: ElementRef,
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
import { Component, input, Input } from '@angular/core';
|
import { Component, input } from '@angular/core';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { Dossier, File } from '@red/domain';
|
import { Dossier, File } from '@red/domain';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
|
||||||
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
||||||
import { StopPropagationDirective } from '@iqser/common-ui';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-documine-export',
|
selector: 'redaction-documine-export',
|
||||||
templateUrl: './documine-export.component.html',
|
templateUrl: './documine-export.component.html',
|
||||||
imports: [MatTooltip, TranslateModule, MatMenuTrigger, MatMenu, MatMenuItem, StopPropagationDirective],
|
imports: [MatTooltip, TranslateModule, MatMenuTrigger, MatMenu, MatMenuItem],
|
||||||
})
|
})
|
||||||
export class DocumineExportComponent {
|
export class DocumineExportComponent {
|
||||||
readonly dossier = input<Dossier>();
|
readonly dossier = input<Dossier>();
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
class="draggable"
|
class="draggable"
|
||||||
svgIcon="red:draggable-dots"
|
svgIcon="red:draggable-dots"
|
||||||
></mat-icon>
|
></mat-icon>
|
||||||
<div [attr.help-mode-key]="'edit_component'" class="iqser-input-group w-full">
|
<div [attr.help-mode-key]="'editor_edit_component'" class="iqser-input-group w-full">
|
||||||
<textarea [id]="'value-input-' + $index" [(ngModel)]="value.value" rows="1" type="text"></textarea>
|
<textarea [id]="'value-input-' + $index" [(ngModel)]="value.value" rows="1" type="text"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
|
||||||
import { AsyncPipe, KeyValuePipe, NgClass, NgForOf, NgIf } from '@angular/common';
|
import { NgClass } from '@angular/common';
|
||||||
import { Component, computed, input, OnInit, output, signal, WritableSignal } from '@angular/core';
|
import { Component, computed, input, OnInit, output, signal, WritableSignal } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
@ -20,16 +20,12 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
NgClass,
|
NgClass,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
KeyValuePipe,
|
|
||||||
CdkDropList,
|
CdkDropList,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
CdkDrag,
|
CdkDrag,
|
||||||
NgIf,
|
|
||||||
NgForOf,
|
|
||||||
CdkDragHandle,
|
CdkDragHandle,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
AsyncPipe,
|
|
||||||
ReplaceNbspPipe,
|
ReplaceNbspPipe,
|
||||||
MatTooltip,
|
MatTooltip,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
<div [ngStyle]="{ height: redactedTextsAreaHeight() }" class="table-container">
|
||||||
|
<table mat-table [dataSource]="source()" multiTemplateDataRows>
|
||||||
|
@for (column of config().columns; track column.label) {
|
||||||
|
<ng-container matColumnDef="{{ column.label }}">
|
||||||
|
<th mat-header-cell class="cell" *matHeaderCellDef>
|
||||||
|
<label>{{ column.value }}</label>
|
||||||
|
</th>
|
||||||
|
<td mat-cell class="cell" [ngStyle]="{ width: column.width ?? 'unset' }" *matCellDef="let cell">
|
||||||
|
{{ cell[column.label] }}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
}
|
||||||
|
<ng-container matColumnDef="expand-icon">
|
||||||
|
<td mat-cell class="expand-icon" *matCellDef="let element" [class.expanded-by-default]="shouldBeExpanded()">
|
||||||
|
@if (!shouldBeExpanded()) {
|
||||||
|
@if (expandedElement === element) {
|
||||||
|
<mat-icon svgIcon="red:arrow-up" (click)="close($event)"></mat-icon>
|
||||||
|
} @else {
|
||||||
|
<mat-icon svgIcon="red:arrow-down" (click)="expand($event, element)"></mat-icon>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<th mat-header-cell *matHeaderCellDef aria-label="row actions"></th>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="expandedDetail">
|
||||||
|
<td mat-cell class="expanded" *matCellDef="let element" [attr.colspan]="columnsToDisplay().length">
|
||||||
|
<div class="expanded-component" [class.expanded-by-default]="shouldBeExpanded()">
|
||||||
|
<ng-container #detailsComponent></ng-container>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<tr mat-header-row *matHeaderRowDef="columnsToDisplay(); sticky: true"></tr>
|
||||||
|
<tr mat-row *matRowDef="let element; columns: columnsToDisplay()"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="component-row"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
td {
|
||||||
|
max-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
overflow-y: auto;
|
||||||
|
transition: height 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-row {
|
||||||
|
height: 20px;
|
||||||
|
max-height: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:nth-child(4n - 3),
|
||||||
|
&:nth-child(4n - 2) {
|
||||||
|
background-color: var(--iqser-alt-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-data-table__cell {
|
||||||
|
height: 20px;
|
||||||
|
padding: 0 8px 0 8px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&.expand-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
max-width: 28px;
|
||||||
|
width: 28px;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded-by-default {
|
||||||
|
width: 0;
|
||||||
|
margin: 0;
|
||||||
|
max-width: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
width: 100%;
|
||||||
|
background: white;
|
||||||
|
padding: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded-component {
|
||||||
|
width: 100%;
|
||||||
|
background: var(--iqser-alt-background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.component-row {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-header-row {
|
||||||
|
height: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.mat-mdc-header-cell {
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expanded-component {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:not(.expanded-by-default) {
|
||||||
|
margin-left: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
opacity: 0.7;
|
||||||
|
font-weight: normal;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell {
|
||||||
|
text-align: start;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
list-style-position: inside;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
padding-right: 8px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
import { Component, computed, effect, input, signal, Type, viewChildren, ViewContainerRef } from '@angular/core';
|
||||||
|
import { NgStyle } from '@angular/common';
|
||||||
|
import {
|
||||||
|
MatCell,
|
||||||
|
MatCellDef,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatRow,
|
||||||
|
MatRowDef,
|
||||||
|
MatTable,
|
||||||
|
} from '@angular/material/table';
|
||||||
|
import { MatIcon } from '@angular/material/icon';
|
||||||
|
import { isJustOne } from '@common-ui/utils';
|
||||||
|
|
||||||
|
export interface Data {
|
||||||
|
styles?: string;
|
||||||
|
component?: Type<unknown>;
|
||||||
|
componentInputs?: { [key: string]: unknown };
|
||||||
|
expanded: boolean;
|
||||||
|
values: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Column {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
width?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
columns: Column[];
|
||||||
|
data: Data[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TABLE_ROW_SIZE = 24;
|
||||||
|
const MAX_ITEMS_DISPLAY = 10;
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-expandable-row-table',
|
||||||
|
standalone: true,
|
||||||
|
imports: [
|
||||||
|
NgStyle,
|
||||||
|
MatTable,
|
||||||
|
MatColumnDef,
|
||||||
|
MatHeaderCell,
|
||||||
|
MatCell,
|
||||||
|
MatIcon,
|
||||||
|
MatHeaderRow,
|
||||||
|
MatRow,
|
||||||
|
MatHeaderCellDef,
|
||||||
|
MatCellDef,
|
||||||
|
MatHeaderRowDef,
|
||||||
|
MatRowDef,
|
||||||
|
],
|
||||||
|
templateUrl: './expandable-row-table.component.html',
|
||||||
|
styleUrl: './expandable-row-table.component.scss',
|
||||||
|
})
|
||||||
|
export class ExpandableRowTableComponent {
|
||||||
|
readonly config = input.required<Config>();
|
||||||
|
readonly source = computed(() => this.config().data.map(row => row.values));
|
||||||
|
readonly columnsToDisplay = computed(() => ['expand-icon', ...this.config().columns.map(column => column.label)]);
|
||||||
|
readonly detailsComponentRef = viewChildren('detailsComponent', { read: ViewContainerRef });
|
||||||
|
readonly shouldBeExpanded = computed(() => isJustOne(this.config().data));
|
||||||
|
readonly redactedTextsAreaHeight = computed(() =>
|
||||||
|
this.shouldBeExpanded()
|
||||||
|
? 'unset'
|
||||||
|
: `${
|
||||||
|
(this.config().data.length <= MAX_ITEMS_DISPLAY
|
||||||
|
? TABLE_ROW_SIZE * this.config().data.length + (this.#currentExpandedComponentHeight() ?? 0)
|
||||||
|
: TABLE_ROW_SIZE * MAX_ITEMS_DISPLAY) + 20
|
||||||
|
}px`,
|
||||||
|
);
|
||||||
|
expandedElement: Record<string, string>;
|
||||||
|
#expandedComponentRef = null;
|
||||||
|
readonly #currentExpandedComponentHeight = signal(null);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
effect(() => {
|
||||||
|
if (this.shouldBeExpanded()) {
|
||||||
|
this.#initializeDetailsComponent(this.source()[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
expand($event: MouseEvent, element: Record<string, string>) {
|
||||||
|
this.expandedElement = element;
|
||||||
|
this.#initializeDetailsComponent(element);
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
close($event: MouseEvent) {
|
||||||
|
this.expandedElement = null;
|
||||||
|
this.#currentExpandedComponentHeight.set(null);
|
||||||
|
this.detailsComponentRef().forEach(ref => ref.clear());
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#initializeDetailsComponent(element: Record<string, string>) {
|
||||||
|
this.detailsComponentRef().forEach(ref => ref.clear());
|
||||||
|
const expandedIndex = this.source().indexOf(element);
|
||||||
|
if (this.detailsComponentRef()[expandedIndex]) {
|
||||||
|
this.#currentExpandedComponentHeight.set(null);
|
||||||
|
const config = this.config().data.find(row => row.values['id'] === element['id']);
|
||||||
|
this.#expandedComponentRef = this.detailsComponentRef()[expandedIndex].createComponent(config.component);
|
||||||
|
if (config.componentInputs) {
|
||||||
|
for (const [key, value] of Object.entries(config.componentInputs)) {
|
||||||
|
(this.#expandedComponentRef.instance as any)[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() =>
|
||||||
|
this.#currentExpandedComponentHeight.set(this.#expandedComponentRef.instance.elementRef.nativeElement.clientHeight),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
<div [class.full-width]="isExpanded()" class="container">
|
||||||
|
<p><strong [translate]="'revert-manual-changes-dialog.details.title'"></strong></p>
|
||||||
|
<redaction-selected-annotations-table
|
||||||
|
[data]="data()"
|
||||||
|
[columns]="columns"
|
||||||
|
[headerHasBackground]="true"
|
||||||
|
></redaction-selected-annotations-table>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
:host {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: 8px;
|
||||||
|
width: calc(100% - 28px);
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.original-container {
|
||||||
|
display: inline-flex;
|
||||||
|
width: calc(100% - 16px);
|
||||||
|
|
||||||
|
p:nth-child(n + 2) {
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
flex: 1;
|
||||||
|
text-align: start;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
list-style-position: inside;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
padding-right: 8px;
|
||||||
|
line-height: 1.5;
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
redaction-selected-annotations-table {
|
||||||
|
::ng-deep .table {
|
||||||
|
padding: 0;
|
||||||
|
width: calc(100% - 16px);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { Component, computed, ElementRef, inject, input } from '@angular/core';
|
||||||
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
import { SelectedAnnotationsTableComponent, ValueColumn } from '../selected-annotations-table/selected-annotations-table.component';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-manual-changes',
|
||||||
|
standalone: true,
|
||||||
|
imports: [SelectedAnnotationsTableComponent, TranslateModule],
|
||||||
|
templateUrl: './manual-changes.component.html',
|
||||||
|
styleUrl: './manual-changes.component.scss',
|
||||||
|
})
|
||||||
|
export class ManualChangesComponent {
|
||||||
|
readonly redaction = input<AnnotationWrapper>();
|
||||||
|
readonly isExpanded = input<boolean>();
|
||||||
|
|
||||||
|
readonly columns = [{ label: 'Action' }, { label: 'Date' }, { label: 'User' }];
|
||||||
|
readonly data = computed(() =>
|
||||||
|
this.redaction().entry.manualChanges.map(
|
||||||
|
change =>
|
||||||
|
[
|
||||||
|
{ label: change.manualRedactionType.toLowerCase().capitalize() },
|
||||||
|
{ label: change.requestedDate, componentType: 'date' },
|
||||||
|
{ label: change.userId, componentType: 'avatar' },
|
||||||
|
] as ValueColumn[],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
readonly elementRef = inject(ElementRef);
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
.ocr-indicator {
|
.ocr-indicator {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-right: 5px;
|
margin-right: 40px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Component, inject, Input } from '@angular/core';
|
import { Component, inject, Input } from '@angular/core';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { OcrProgressBarComponent } from '@shared/components/ocr-progress-bar/ocr-progress-bar.component';
|
import { OcrProgressBarComponent } from '@shared/components/ocr-progress-bar/ocr-progress-bar.component';
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { MatProgressBar } from '@angular/material/progress-bar';
|
import { MatProgressBar } from '@angular/material/progress-bar';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -11,7 +10,7 @@ import { MatTooltip } from '@angular/material/tooltip';
|
|||||||
selector: 'redaction-readonly-banner',
|
selector: 'redaction-readonly-banner',
|
||||||
templateUrl: './readonly-banner.component.html',
|
templateUrl: './readonly-banner.component.html',
|
||||||
styleUrls: ['./readonly-banner.component.scss'],
|
styleUrls: ['./readonly-banner.component.scss'],
|
||||||
imports: [OcrProgressBarComponent, NgIf, MatProgressBar, MatIcon, TranslateModule, MatTooltip],
|
imports: [OcrProgressBarComponent, MatProgressBar, MatIcon, TranslateModule, MatTooltip],
|
||||||
})
|
})
|
||||||
export class ReadonlyBannerComponent {
|
export class ReadonlyBannerComponent {
|
||||||
protected readonly _state = inject(FilePreviewStateService);
|
protected readonly _state = inject(FilePreviewStateService);
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
<div [ngStyle]="gridConfig()" class="table">
|
<div [ngStyle]="gridConfig()" class="table">
|
||||||
@for (column of _columns(); track column.label) {
|
@for (column of _columns(); track column.label) {
|
||||||
<div [ngClass]="{ hide: !!column.hide }" class="col cell">
|
<div [ngClass]="{ hide: !!column.hide, background: headerHasBackground() }" class="col cell">
|
||||||
<label>{{ column.label }}</label>
|
<label>{{ column.label }}</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@for (row of _data(); track $index) {
|
@for (row of _data(); track $index) {
|
||||||
@for (cell of row; track cell.label) {
|
@for (cell of row; track cell.label) {
|
||||||
<div [ngClass]="{ background: _data().indexOf(row) % 2 === 0, hide: !!cell.hide, bold: cell.bold }" class="cell">
|
<div [ngClass]="{ background: _data().indexOf(row) % 2 === 0, hide: !!cell.hide, bold: cell.bold }" class="cell">
|
||||||
{{ cell.label }}
|
@if (cell.componentType === 'date') {
|
||||||
|
<span>{{ cell.label | date }}</span>
|
||||||
|
} @else if (cell.componentType === 'avatar') {
|
||||||
|
<iqser-initials-avatar [user]="cell.label" [withName]="true" [size]="'extra-small'"></iqser-initials-avatar>
|
||||||
|
} @else {
|
||||||
|
{{ cell.label }}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,10 @@
|
|||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
background: white;
|
|
||||||
|
&:not(.background) {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
@ -26,6 +29,10 @@
|
|||||||
list-style-position: inside;
|
list-style-position: inside;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:not(.background) {
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import { Component, computed, input } from '@angular/core';
|
import { Component, computed, input } from '@angular/core';
|
||||||
import { NgClass, NgForOf, NgStyle } from '@angular/common';
|
import { NgClass, NgStyle } from '@angular/common';
|
||||||
|
import { InitialsAvatarComponent } from '@common-ui/users';
|
||||||
|
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||||
|
|
||||||
export interface ValueColumn {
|
export interface ValueColumn {
|
||||||
label: string;
|
label: string;
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
bold?: boolean;
|
bold?: boolean;
|
||||||
width?: string;
|
width?: string;
|
||||||
|
componentType?: 'string' | 'avatar' | 'date';
|
||||||
}
|
}
|
||||||
|
|
||||||
const TABLE_ROW_SIZE = 20;
|
const TABLE_ROW_SIZE = 20;
|
||||||
@ -13,13 +16,13 @@ const MAX_ITEMS_DISPLAY = 10;
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-selected-annotations-table',
|
selector: 'redaction-selected-annotations-table',
|
||||||
imports: [NgForOf, NgClass, NgStyle],
|
imports: [NgClass, NgStyle, InitialsAvatarComponent, DatePipe],
|
||||||
templateUrl: './selected-annotations-table.component.html',
|
templateUrl: './selected-annotations-table.component.html',
|
||||||
styleUrl: './selected-annotations-table.component.scss',
|
styleUrl: './selected-annotations-table.component.scss',
|
||||||
})
|
})
|
||||||
export class SelectedAnnotationsTableComponent {
|
export class SelectedAnnotationsTableComponent {
|
||||||
readonly defaultColumnWidth = input(false);
|
readonly defaultColumnWidth = input(false);
|
||||||
|
readonly headerHasBackground = input(false);
|
||||||
readonly columns = input.required<ValueColumn[]>();
|
readonly columns = input.required<ValueColumn[]>();
|
||||||
readonly _columns = computed(() => this.columns().filter(item => !this.defaultColumnWidth() || !item.hide));
|
readonly _columns = computed(() => this.columns().filter(item => !this.defaultColumnWidth() || !item.hide));
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<iqser-popup-filter [primaryFiltersSlug]="'componentLogFilters'" [attr.help-mode-key]="'filter_components'"></iqser-popup-filter>
|
<iqser-popup-filter [primaryFiltersSlug]="'componentLogFilters'" [attr.help-mode-key]="'filter_components'"></iqser-popup-filter>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="componentLogService.all$ | async as components" class="components-container" id="components-view">
|
<div class="components-container" id="components-view">
|
||||||
<div class="component-row">
|
<div class="component-row">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="component">{{ 'component-management.table-header.component' | translate }}</div>
|
<div class="component">{{ 'component-management.table-header.component' | translate }}</div>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<div class="row-separator"></div>
|
<div class="row-separator"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let entry of components" class="component-row">
|
<div *ngFor="let entry of filteredComponents()" class="component-row">
|
||||||
<redaction-editable-structured-component-value
|
<redaction-editable-structured-component-value
|
||||||
#editableComponent
|
#editableComponent
|
||||||
[entry]="entry"
|
[entry]="entry"
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { Component, effect, Input, OnInit, signal, ViewChildren } from '@angular/core';
|
import { Component, computed, effect, Input, OnInit, ViewChildren } from '@angular/core';
|
||||||
import { List } from '@common-ui/utils';
|
import { List } from '@common-ui/utils';
|
||||||
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||||
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
|
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
|
||||||
import { combineLatest, firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, map } from 'rxjs';
|
||||||
import { EditableStructuredComponentValueComponent } from '../editable-structured-component-value/editable-structured-component-value.component';
|
import { EditableStructuredComponentValueComponent } from '../editable-structured-component-value/editable-structured-component-value.component';
|
||||||
import { FilterService, PopupFilterComponent } from '@common-ui/filtering';
|
import { FilterService, PopupFilterComponent } from '@common-ui/filtering';
|
||||||
import { ComponentLogFilterService } from '../../services/component-log-filter.service';
|
import { ComponentLogFilterService } from '../../services/component-log-filter.service';
|
||||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
import { NgForOf } from '@angular/common';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
||||||
|
import { toSignal } from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-structured-component-management',
|
selector: 'redaction-structured-component-management',
|
||||||
templateUrl: './structured-component-management.component.html',
|
templateUrl: './structured-component-management.component.html',
|
||||||
styleUrls: ['./structured-component-management.component.scss'],
|
styleUrls: ['./structured-component-management.component.scss'],
|
||||||
imports: [PopupFilterComponent, NgIf, AsyncPipe, TranslateModule, NgForOf, EditableStructuredComponentValueComponent],
|
imports: [PopupFilterComponent, TranslateModule, NgForOf, EditableStructuredComponentValueComponent],
|
||||||
})
|
})
|
||||||
export class StructuredComponentManagementComponent implements OnInit {
|
export class StructuredComponentManagementComponent implements OnInit {
|
||||||
protected readonly iconButtonTypes = IconButtonTypes;
|
protected readonly iconButtonTypes = IconButtonTypes;
|
||||||
@ -23,6 +24,12 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
@Input() dictionaries: Dictionary[];
|
@Input() dictionaries: Dictionary[];
|
||||||
@ViewChildren('editableComponent') editableComponents: List<EditableStructuredComponentValueComponent>;
|
@ViewChildren('editableComponent') editableComponents: List<EditableStructuredComponentValueComponent>;
|
||||||
|
|
||||||
|
readonly filteredComponents = computed(() => this.#filteredComponents);
|
||||||
|
readonly #displayedComponents = toSignal(this.componentLogService.all$);
|
||||||
|
readonly #filterModel = toSignal(
|
||||||
|
this._filterService.getFilterModels$('componentLogFilters').pipe(map(filters => (filters ? [...filters] : []))),
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _componentLogFilterService: ComponentLogFilterService,
|
private readonly _componentLogFilterService: ComponentLogFilterService,
|
||||||
@ -40,6 +47,13 @@ export class StructuredComponentManagementComponent implements OnInit {
|
|||||||
return this.file.workflowStatus !== WorkflowFileStatuses.APPROVED;
|
return this.file.workflowStatus !== WorkflowFileStatuses.APPROVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get #filteredComponents() {
|
||||||
|
if (this.#filterModel() && this.#displayedComponents()) {
|
||||||
|
return this._componentLogFilterService.filterComponents(this.#displayedComponents(), this.#filterModel());
|
||||||
|
}
|
||||||
|
return this.#displayedComponents();
|
||||||
|
}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this.#loadData();
|
await this.#loadData();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="fileAssignService.assignToMe([file])"
|
(action)="assignToMe(file)"
|
||||||
*ngIf="_canAssignToSelf()"
|
*ngIf="_canAssignToSelf()"
|
||||||
[icon]="'red:assign-me'"
|
[icon]="'red:assign-me'"
|
||||||
[tooltip]="'file-preview.assign-me' | translate"
|
[tooltip]="'file-preview.assign-me' | translate"
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { Component, computed, HostListener, NgZone, OnDestroy, OnInit } from '@angular/core';
|
import { NgIf } from '@angular/common';
|
||||||
|
import { Component, computed, HostListener, inject, NgZone, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { StatusBarComponent } from '@common-ui/shared';
|
||||||
|
import { Bind } from '@common-ui/utils';
|
||||||
import { CircleButtonComponent, LoadingService, StopPropagationDirective, Toaster } from '@iqser/common-ui';
|
import { CircleButtonComponent, LoadingService, StopPropagationDirective, Toaster } from '@iqser/common-ui';
|
||||||
import { getCurrentUser, InitialsAvatarComponent } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser, InitialsAvatarComponent } from '@iqser/common-ui/lib/users';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { File, User } from '@red/domain';
|
import { File, User } from '@red/domain';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { AssignUserDropdownComponent } from '@shared/components/assign-user-dropdown/assign-user-dropdown.component';
|
||||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { moveElementInArray } from '@utils/functions';
|
import { moveElementInArray } from '@utils/functions';
|
||||||
import { FileAssignService } from '../../../shared-dossiers/services/file-assign.service';
|
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
|
||||||
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||||
import { Bind } from '@common-ui/utils';
|
import { FileAssignService } from '../../../shared-dossiers/services/file-assign.service';
|
||||||
import { AssignUserDropdownComponent } from '@shared/components/assign-user-dropdown/assign-user-dropdown.component';
|
import { FileDataService } from '../../services/file-data.service';
|
||||||
import { NgIf } from '@angular/common';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { StatusBarComponent } from '@common-ui/shared';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-management',
|
selector: 'redaction-user-management',
|
||||||
@ -48,6 +49,7 @@ export class UserManagementComponent implements OnInit, OnDestroy {
|
|||||||
: this.#customSort([...dossier.memberIds, ...unassignUser]);
|
: this.#customSort([...dossier.memberIds, ...unassignUser]);
|
||||||
});
|
});
|
||||||
protected readonly _currentUserId = getCurrentUser().id;
|
protected readonly _currentUserId = getCurrentUser().id;
|
||||||
|
protected readonly fileDataService = inject(FileDataService);
|
||||||
readonly translations = workflowFileStatusTranslations;
|
readonly translations = workflowFileStatusTranslations;
|
||||||
readonly statusBarConfig = computed(() => [{ length: 1, color: this.state.file().workflowStatus }]);
|
readonly statusBarConfig = computed(() => [{ length: 1, color: this.state.file().workflowStatus }]);
|
||||||
readonly assignTooltip = computed(() => {
|
readonly assignTooltip = computed(() => {
|
||||||
@ -71,6 +73,13 @@ export class UserManagementComponent implements OnInit, OnDestroy {
|
|||||||
readonly ngZone: NgZone,
|
readonly ngZone: NgZone,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async assignToMe(file: File) {
|
||||||
|
await this.fileAssignService.assignToMe([file]);
|
||||||
|
//TODO: check which one to call
|
||||||
|
// await firstValueFrom(this.fileDataService.updateAnnotations(file, file.numberOfAnalyses));
|
||||||
|
await this.fileDataService.loadEntityLog();
|
||||||
|
}
|
||||||
|
|
||||||
async assignReviewer(file: File, user: User | string) {
|
async assignReviewer(file: File, user: User | string) {
|
||||||
const assigneeId = typeof user === 'string' ? user : user?.id;
|
const assigneeId = typeof user === 'string' ? user : user?.id;
|
||||||
|
|
||||||
@ -84,6 +93,11 @@ export class UserManagementComponent implements OnInit, OnDestroy {
|
|||||||
await this.filesService.setUnderApproval(file, assigneeId);
|
await this.filesService.setUnderApproval(file, assigneeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (assigneeId === this._currentUserId) {
|
||||||
|
// await firstValueFrom(this.fileDataService.updateAnnotations(file, file.numberOfAnalyses));
|
||||||
|
await this.fileDataService.loadEntityLog();
|
||||||
|
}
|
||||||
|
|
||||||
this.loadingService.stop();
|
this.loadingService.stop();
|
||||||
|
|
||||||
const translateParams = { reviewerName: this.userService.getName(assigneeId), filename: file.filename };
|
const translateParams = { reviewerName: this.userService.getName(assigneeId), filename: file.filename };
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { Dossier } from '@red/domain';
|
|||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgForOf, NgIf } from '@angular/common';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -18,7 +18,6 @@ import { LegalBasisOption } from '../../utils/dialog-types';
|
|||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
NgForOf,
|
NgForOf,
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { CircleButtonComponent, ConfirmOptions, IconButtonComponent, IqserDialog
|
|||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||||
import { IComponentLogEntry } from '@red/domain';
|
import { IComponentLogEntry } from '@red/domain';
|
||||||
import { NgFor, NgIf } from '@angular/common';
|
import { NgFor } from '@angular/common';
|
||||||
|
|
||||||
interface RevertValueData {
|
interface RevertValueData {
|
||||||
entry: IComponentLogEntry;
|
entry: IComponentLogEntry;
|
||||||
@ -13,7 +13,7 @@ interface RevertValueResult {}
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: 'revert-value-dialog.component.html',
|
templateUrl: 'revert-value-dialog.component.html',
|
||||||
styleUrls: ['./revert-value-dialog.component.scss'],
|
styleUrls: ['./revert-value-dialog.component.scss'],
|
||||||
imports: [CircleButtonComponent, IconButtonComponent, MatDialogClose, TranslateModule, NgFor, NgIf],
|
imports: [CircleButtonComponent, IconButtonComponent, MatDialogClose, TranslateModule, NgFor],
|
||||||
})
|
})
|
||||||
export class RevertValueDialogComponent extends IqserDialogComponent<RevertValueDialogComponent, RevertValueData, RevertValueResult> {
|
export class RevertValueDialogComponent extends IqserDialogComponent<RevertValueDialogComponent, RevertValueData, RevertValueResult> {
|
||||||
protected readonly entry = this.data.entry;
|
protected readonly entry = this.data.entry;
|
||||||
|
|||||||
@ -15,12 +15,10 @@ import { JustificationsService } from '@services/entity-services/justifications.
|
|||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
import { MatFormField } from '@angular/material/form-field';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { MatCheckbox } from '@angular/material/checkbox';
|
|
||||||
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
|
||||||
import {
|
import {
|
||||||
LegalBasisOption,
|
LegalBasisOption,
|
||||||
@ -41,18 +39,14 @@ export const NON_READABLE_CONTENT = 'non-readable content';
|
|||||||
styleUrls: ['./rectangle-annotation-dialog.component.scss'],
|
styleUrls: ['./rectangle-annotation-dialog.component.scss'],
|
||||||
imports: [
|
imports: [
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
NgIf,
|
|
||||||
CircleButtonComponent,
|
CircleButtonComponent,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
HasScrollbarDirective,
|
HasScrollbarDirective,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatSelectTrigger,
|
|
||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
NgForOf,
|
|
||||||
MatTooltip,
|
MatTooltip,
|
||||||
IqserDenyDirective,
|
IqserDenyDirective,
|
||||||
MatCheckbox,
|
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
DetailsRadioComponent,
|
DetailsRadioComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
<section class="dialog">
|
||||||
|
<div class="dialog-header heading-l">
|
||||||
|
<span [translate]="'revert-manual-changes-dialog.title'"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-content" [class.is-expanded]="data.redactions.length === 1">
|
||||||
|
<div class="iqser-input-group">
|
||||||
|
<redaction-expandable-row-table [config]="config"></redaction-expandable-row-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<iqser-icon-button
|
||||||
|
[label]="'revert-manual-changes-dialog.actions.save' | translate"
|
||||||
|
[type]="IconButtonTypes.primary"
|
||||||
|
(click)="save()"
|
||||||
|
></iqser-icon-button>
|
||||||
|
<div [translate]="'revert-manual-changes-dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close" mat-dialog-close />
|
||||||
|
</section>
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
.dialog-content:not(.is-expanded) {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
import { Component, signal } from '@angular/core';
|
||||||
|
import { CircleButtonComponent, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||||
|
import { RevertManualChangesData } from '../../utils/dialog-types';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { Config, ExpandableRowTableComponent } from '../../components/expandable-row-table/expandable-row-table.component';
|
||||||
|
import { ManualChangesComponent } from '../../components/manual-changes/manual-changes.component';
|
||||||
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
|
import { isJustOne } from '@common-ui/utils';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-revert-manual-changes-dialog',
|
||||||
|
standalone: true,
|
||||||
|
imports: [IconButtonComponent, TranslateModule, CircleButtonComponent, ExpandableRowTableComponent, MatDialogClose],
|
||||||
|
templateUrl: './revert-manual-changes-dialog.component.html',
|
||||||
|
styleUrl: './revert-manual-changes-dialog.component.scss',
|
||||||
|
})
|
||||||
|
export class RevertManualChangesDialogComponent extends IqserDialogComponent<RevertManualChangesData, RevertManualChangesData, boolean> {
|
||||||
|
protected readonly IconButtonTypes = IconButtonTypes;
|
||||||
|
|
||||||
|
readonly config: Config = {
|
||||||
|
columns: [
|
||||||
|
{ label: 'value', value: 'Value', width: '25%' },
|
||||||
|
{ label: 'type', value: 'Type', width: '25%' },
|
||||||
|
{
|
||||||
|
label: 'reason',
|
||||||
|
value: 'Reason',
|
||||||
|
width: '50%',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: this.data.redactions.map(redaction => ({
|
||||||
|
values: {
|
||||||
|
id: redaction.id,
|
||||||
|
value: redaction.value,
|
||||||
|
type: redaction.typeLabel,
|
||||||
|
reason: redaction.legalBasisValue,
|
||||||
|
},
|
||||||
|
expanded: false,
|
||||||
|
component: ManualChangesComponent,
|
||||||
|
componentInputs: { redaction: signal(redaction), isExpanded: signal(isJustOne(this.data.redactions)) },
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.close(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import { AnnotationActionsService } from './services/annotation-actions.service'
|
|||||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||||
import { AnnotationsListingService } from './services/annotations-listing.service';
|
import { AnnotationsListingService } from './services/annotations-listing.service';
|
||||||
|
import { ComponentLogFilterService } from './services/component-log-filter.service';
|
||||||
import { DocumentInfoService } from './services/document-info.service';
|
import { DocumentInfoService } from './services/document-info.service';
|
||||||
import { ExcludedPagesService } from './services/excluded-pages.service';
|
import { ExcludedPagesService } from './services/excluded-pages.service';
|
||||||
import { FileDataService } from './services/file-data.service';
|
import { FileDataService } from './services/file-data.service';
|
||||||
@ -16,7 +17,6 @@ import { PdfProxyService } from './services/pdf-proxy.service';
|
|||||||
import { SkippedService } from './services/skipped.service';
|
import { SkippedService } from './services/skipped.service';
|
||||||
import { StampService } from './services/stamp.service';
|
import { StampService } from './services/stamp.service';
|
||||||
import { ViewModeService } from './services/view-mode.service';
|
import { ViewModeService } from './services/view-mode.service';
|
||||||
import { ComponentLogFilterService } from './services/component-log-filter.service';
|
|
||||||
|
|
||||||
export const filePreviewScreenProviders = [
|
export const filePreviewScreenProviders = [
|
||||||
FilterService,
|
FilterService,
|
||||||
|
|||||||
@ -58,9 +58,9 @@ redaction-pdf-viewer.hidden {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.resize {
|
.resize {
|
||||||
background: #444857;
|
background: var(--iqser-grey-4);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 14px;
|
width: 10px;
|
||||||
cursor: col-resize;
|
cursor: col-resize;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
@ -74,7 +74,7 @@ redaction-pdf-viewer.hidden {
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
border-inline: 1px solid #fff;
|
border-inline: 1px solid var(--iqser-grey-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 1015px) {
|
@media only screen and (max-width: 1015px) {
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
import { CdkDrag, CdkDragEnd, CdkDragMove, CdkDragStart } from '@angular/cdk/drag-drop';
|
||||||
|
import { NgIf } from '@angular/common';
|
||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Component,
|
Component,
|
||||||
effect,
|
effect,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
|
inject,
|
||||||
NgZone,
|
NgZone,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
OnInit,
|
OnInit,
|
||||||
@ -13,6 +15,7 @@ import {
|
|||||||
viewChild,
|
viewChild,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
import { ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
||||||
import {
|
import {
|
||||||
@ -29,21 +32,25 @@ import {
|
|||||||
Toaster,
|
Toaster,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
import { copyLocalStorageFiltersValues, FilterService, INestedFilter, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
|
import { copyLocalStorageFiltersValues, FilterService, INestedFilter, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
|
||||||
import { AutoUnsubscribe, Bind, bool, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
|
import { AutoUnsubscribe, Bind, bool, List, log, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { ManualRedactionEntryTypes, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
import { ManualRedactionEntryTypes, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||||
import { File, ViewModes } from '@red/domain';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { AnalyseStatuses, AnalysisEvent, File, ViewModes, WsTopics } from '@red/domain';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
|
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
||||||
import { FilesMapService } from '@services/files/files-map.service';
|
import { FilesMapService } from '@services/files/files-map.service';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { ReanalysisService } from '@services/reanalysis.service';
|
import { ReanalysisService } from '@services/reanalysis.service';
|
||||||
|
import { WebSocketService } from '@services/web-socket.service';
|
||||||
|
import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component';
|
||||||
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 { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { combineLatest, first, firstValueFrom, Observable, of, pairwise } from 'rxjs';
|
import { combineLatest, first, firstValueFrom, Observable, of, pairwise, Subscription } from 'rxjs';
|
||||||
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { byId, byPage, handleFilterDelta, hasChanges } from '../../utils';
|
import { byId, byPage, handleFilterDelta, hasChanges } from '../../utils';
|
||||||
import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service';
|
import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service';
|
||||||
@ -54,33 +61,28 @@ import { PdfViewer } from '../pdf-viewer/services/pdf-viewer.service';
|
|||||||
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
|
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
|
||||||
import { ViewerHeaderService } from '../pdf-viewer/services/viewer-header.service';
|
import { ViewerHeaderService } from '../pdf-viewer/services/viewer-header.service';
|
||||||
import { ROTATION_ACTION_BUTTONS, ViewerEvents } from '../pdf-viewer/utils/constants';
|
import { ROTATION_ACTION_BUTTONS, ViewerEvents } from '../pdf-viewer/utils/constants';
|
||||||
|
import { FileHeaderComponent } from './components/file-header/file-header.component';
|
||||||
|
import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component';
|
||||||
|
import { StructuredComponentManagementComponent } from './components/structured-component-management/structured-component-management.component';
|
||||||
import { AddHintDialogComponent } from './dialogs/add-hint-dialog/add-hint-dialog.component';
|
import { AddHintDialogComponent } from './dialogs/add-hint-dialog/add-hint-dialog.component';
|
||||||
import { AddAnnotationDialogComponent } from './dialogs/docu-mine/add-annotation-dialog/add-annotation-dialog.component';
|
import { AddAnnotationDialogComponent } from './dialogs/docu-mine/add-annotation-dialog/add-annotation-dialog.component';
|
||||||
|
import { RectangleAnnotationDialog } from './dialogs/rectangle-annotation-dialog/rectangle-annotation-dialog.component';
|
||||||
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
|
import { RedactTextDialogComponent } from './dialogs/redact-text-dialog/redact-text-dialog.component';
|
||||||
import { filePreviewScreenProviders } from './file-preview-providers';
|
import { filePreviewScreenProviders } from './file-preview-providers';
|
||||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||||
import { AnnotationsListingService } from './services/annotations-listing.service';
|
import { AnnotationsListingService } from './services/annotations-listing.service';
|
||||||
|
import { DocumentInfoService } from './services/document-info.service';
|
||||||
import { FileDataService } from './services/file-data.service';
|
import { FileDataService } from './services/file-data.service';
|
||||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||||
|
import { MultiSelectService } from './services/multi-select.service';
|
||||||
import { PdfProxyService } from './services/pdf-proxy.service';
|
import { PdfProxyService } from './services/pdf-proxy.service';
|
||||||
import { SkippedService } from './services/skipped.service';
|
import { SkippedService } from './services/skipped.service';
|
||||||
import { StampService } from './services/stamp.service';
|
import { StampService } from './services/stamp.service';
|
||||||
import { ViewModeService } from './services/view-mode.service';
|
import { ViewModeService } from './services/view-mode.service';
|
||||||
import { RedactTextData } from './utils/dialog-types';
|
|
||||||
import { MultiSelectService } from './services/multi-select.service';
|
|
||||||
import { NgIf } from '@angular/common';
|
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
|
||||||
import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component';
|
|
||||||
import { TypeFilterComponent } from '@shared/components/type-filter/type-filter.component';
|
|
||||||
import { FileHeaderComponent } from './components/file-header/file-header.component';
|
|
||||||
import { StructuredComponentManagementComponent } from './components/structured-component-management/structured-component-management.component';
|
|
||||||
import { DocumentInfoService } from './services/document-info.service';
|
|
||||||
import { RectangleAnnotationDialog } from './dialogs/rectangle-annotation-dialog/rectangle-annotation-dialog.component';
|
|
||||||
import { ANNOTATION_ACTION_ICONS, ANNOTATION_ACTIONS } from './utils/constants';
|
import { ANNOTATION_ACTION_ICONS, ANNOTATION_ACTIONS } from './utils/constants';
|
||||||
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
import { RedactTextData } from './utils/dialog-types';
|
||||||
import { CdkDrag, CdkDragEnd, CdkDragMove, CdkDragStart } from '@angular/cdk/drag-drop';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
templateUrl: './file-preview-screen.component.html',
|
templateUrl: './file-preview-screen.component.html',
|
||||||
@ -99,18 +101,28 @@ import { CdkDrag, CdkDragEnd, CdkDragMove, CdkDragStart } from '@angular/cdk/dra
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate {
|
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate {
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
|
||||||
readonly roles = Roles;
|
|
||||||
readonly fileId = this.state.fileId;
|
|
||||||
readonly dossierId = this.state.dossierId;
|
|
||||||
readonly resizeHandle = viewChild<ElementRef>('resize');
|
|
||||||
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
@ViewChild('annotationFilterTemplate', {
|
@ViewChild('annotationFilterTemplate', {
|
||||||
read: TemplateRef,
|
read: TemplateRef,
|
||||||
static: false,
|
static: false,
|
||||||
})
|
})
|
||||||
private readonly _filterTemplate: TemplateRef<unknown>;
|
private readonly _filterTemplate: TemplateRef<unknown>;
|
||||||
#loadAllAnnotationsEnabled = false;
|
#loadAllAnnotationsEnabled = false;
|
||||||
|
readonly #wsConnection$ = inject(WebSocketService)
|
||||||
|
.listen<AnalysisEvent>(WsTopics.ANALYSIS)
|
||||||
|
.pipe(
|
||||||
|
log('[WS] Analysis events'),
|
||||||
|
filter(event => event.analyseStatus === AnalyseStatuses.FINISHED),
|
||||||
|
switchMap(event => this._fileDataService.updateAnnotations(this.state.file(), event.analysisNumber)),
|
||||||
|
log('[WS] Annotations updated'),
|
||||||
|
);
|
||||||
|
#wsConnectionSub: Subscription;
|
||||||
|
protected readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
|
readonly roles = Roles;
|
||||||
|
readonly fileId = this.state.fileId;
|
||||||
|
readonly dossierId = this.state.dossierId;
|
||||||
|
readonly resizeHandle = viewChild<ElementRef>('resize');
|
||||||
|
#overlayElement: HTMLDivElement | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly pdf: PdfViewer,
|
readonly pdf: PdfViewer,
|
||||||
@ -152,11 +164,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
private readonly _componentLogService: ComponentLogService,
|
private readonly _componentLogService: ComponentLogService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
effect(() => {
|
|
||||||
const file = this.state.file();
|
|
||||||
this._fileDataService.loadAnnotations(file).then();
|
|
||||||
});
|
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const file = this.state.file();
|
const file = this.state.file();
|
||||||
if (file.analysisRequired && !file.excludedFromAutomaticAnalysis) {
|
if (file.analysisRequired && !file.excludedFromAutomaticAnalysis) {
|
||||||
@ -164,16 +171,20 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
effect(
|
const file = this.state.file();
|
||||||
() => {
|
if (this._fileDataService.annotations().length) {
|
||||||
if (this._documentViewer.loaded()) {
|
firstValueFrom(this._fileDataService.updateAnnotations(file, file.numberOfAnalyses)).then();
|
||||||
this._pageRotationService.clearRotations();
|
} else {
|
||||||
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
|
this._fileDataService.loadAnnotations(file).then();
|
||||||
this.viewerReady().then();
|
}
|
||||||
}
|
|
||||||
},
|
effect(() => {
|
||||||
{ allowSignalWrites: true },
|
if (this._documentViewer.loaded()) {
|
||||||
);
|
this._pageRotationService.clearRotations();
|
||||||
|
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
|
||||||
|
this.viewerReady().then();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
this.state.updateExcludedPagesStyle();
|
this.state.updateExcludedPagesStyle();
|
||||||
@ -239,10 +250,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
onDragStart(event: CdkDragStart) {
|
onDragStart(event: CdkDragStart) {
|
||||||
event.event.preventDefault();
|
event.event.preventDefault();
|
||||||
if (!this.isDocumine) return;
|
if (!this.isDocumine) return;
|
||||||
const contentInnerElement = document.body.getElementsByClassName('content-inner').item(0) as HTMLElement;
|
this.#createDragOverlay();
|
||||||
if (contentInnerElement) {
|
|
||||||
contentInnerElement.classList.add('dragging');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDragMove(event: CdkDragMove) {
|
onDragMove(event: CdkDragMove) {
|
||||||
@ -256,10 +264,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
onDragEnd(event: CdkDragEnd) {
|
onDragEnd(event: CdkDragEnd) {
|
||||||
event.event.preventDefault();
|
event.event.preventDefault();
|
||||||
if (!this.isDocumine) return;
|
if (!this.isDocumine) return;
|
||||||
const contentInnerElement = document.body.getElementsByClassName('content-inner').item(0) as HTMLElement;
|
this.#removeDragOverlay();
|
||||||
if (contentInnerElement) {
|
|
||||||
contentInnerElement.classList.remove('dragging');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteEarmarksOnViewChange$() {
|
deleteEarmarksOnViewChange$() {
|
||||||
@ -286,17 +291,19 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
this.pdf.instance.UI.hotkeys.off('esc');
|
this.pdf.instance.UI.hotkeys.off('esc');
|
||||||
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
|
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
|
||||||
this._changeRef.markForCheck();
|
this._changeRef.markForCheck();
|
||||||
|
this.#wsConnectionSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.pdf.instance.UI.hotkeys.off('esc');
|
this.pdf.instance.UI.hotkeys.off('esc');
|
||||||
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
|
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
|
||||||
super.ngOnDestroy();
|
super.ngOnDestroy();
|
||||||
|
this.#wsConnectionSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEscInsideViewer($event: KeyboardEvent) {
|
handleEscInsideViewer($event: KeyboardEvent) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
if (!!this._annotationManager.selected[0]) {
|
if (this._annotationManager.selected[0]) {
|
||||||
const doesHaveWrapper = this._fileDataService.find(this._annotationManager.selected[0]?.Id);
|
const doesHaveWrapper = this._fileDataService.find(this._annotationManager.selected[0]?.Id);
|
||||||
if (!doesHaveWrapper) {
|
if (!doesHaveWrapper) {
|
||||||
this._annotationManager.delete(this._annotationManager.selected[0]?.Id);
|
this._annotationManager.delete(this._annotationManager.selected[0]?.Id);
|
||||||
@ -317,24 +324,17 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
@Bind()
|
@Bind()
|
||||||
handleViewerClick(event: MouseEvent) {
|
handleViewerClick(event: MouseEvent) {
|
||||||
this._ngZone.run(() => {
|
this._ngZone.run(() => {
|
||||||
if (event.isTrusted) {
|
if (!event.isTrusted) return;
|
||||||
const clickedElement = event.target as HTMLElement;
|
const clickedElement = event.target as HTMLElement;
|
||||||
const actionIconClicked = ANNOTATION_ACTION_ICONS.some(action =>
|
const actionIconClicked = ANNOTATION_ACTION_ICONS.some(action => (clickedElement as HTMLImageElement).src?.includes(action));
|
||||||
(clickedElement as HTMLImageElement).src?.includes(action),
|
const actionClicked = ANNOTATION_ACTIONS.some(action => clickedElement.getAttribute('aria-label')?.includes(action));
|
||||||
);
|
if (!this._multiSelectService.active() || actionIconClicked || actionClicked) return;
|
||||||
const actionClicked = ANNOTATION_ACTIONS.some(action => clickedElement.getAttribute('aria-label')?.includes(action));
|
if (clickedElement.querySelector('#selectionrect') || clickedElement.id === `pageWidgetContainer${this.pdf.currentPage()}`) {
|
||||||
if (this._multiSelectService.active() && !actionIconClicked && !actionClicked) {
|
if (!this._annotationManager.selected.length) {
|
||||||
if (
|
this._multiSelectService.deactivate();
|
||||||
clickedElement.querySelector('#selectionrect') ||
|
|
||||||
clickedElement.id === `pageWidgetContainer${this.pdf.currentPage()}`
|
|
||||||
) {
|
|
||||||
if (!this._annotationManager.selected.length) {
|
|
||||||
this._multiSelectService.deactivate();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._multiSelectService.deactivate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this._multiSelectService.deactivate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -354,6 +354,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.#wsConnectionSub = this.#wsConnection$.subscribe();
|
||||||
this.#updateViewerPosition();
|
this.#updateViewerPosition();
|
||||||
const file = this.state.file();
|
const file = this.state.file();
|
||||||
|
|
||||||
@ -455,7 +456,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
#getPixelsInPercentage(pixels: number) {
|
#getPixelsInPercentage(pixels: number) {
|
||||||
return (pixels / window.screen.width) * 100;
|
return (pixels / document.body.getBoundingClientRect().width) * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
async #updateViewMode(): Promise<void> {
|
async #updateViewMode(): Promise<void> {
|
||||||
@ -886,32 +887,30 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
const components = this._componentLogService.all;
|
const components = this._componentLogService.all;
|
||||||
const filteredComponentIds = untracked(this.state.componentReferenceIds);
|
const filteredComponentIds = untracked(this.state.componentReferenceIds);
|
||||||
|
|
||||||
if (filteredComponentIds && annotationFilters) {
|
if (!filteredComponentIds || !annotationFilters) return annotationFilters;
|
||||||
const filteredComponentIdsSet = new Set(filteredComponentIds);
|
|
||||||
|
|
||||||
const references = new Set<string>();
|
const filteredComponentIdsSet = new Set(filteredComponentIds);
|
||||||
for (const component of components) {
|
|
||||||
for (const componentValue of component.componentValues) {
|
const references = new Set<string>();
|
||||||
for (const reference of componentValue.entityReferences) {
|
for (const component of components) {
|
||||||
if (filteredComponentIdsSet.has(reference.id)) {
|
for (const componentValue of component.componentValues) {
|
||||||
references.add(reference.type);
|
for (const reference of componentValue.entityReferences) {
|
||||||
}
|
if (filteredComponentIdsSet.has(reference.id)) {
|
||||||
|
references.add(reference.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return annotationFilters
|
|
||||||
.map(filter => {
|
|
||||||
const filteredChildren = filter.children.filter(c => references.has(c.label.replace(/ /g, '_').toLowerCase()));
|
|
||||||
if (filteredChildren.length) {
|
|
||||||
return { ...filter, children: filteredChildren };
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
.filter(f => f !== null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return annotationFilters;
|
return annotationFilters
|
||||||
|
.map(filter => {
|
||||||
|
const filteredChildren = filter.children.filter(c => references.has(c.label.replace(/ /g, '_').toLowerCase()));
|
||||||
|
if (filteredChildren.length) {
|
||||||
|
return { ...filter, children: filteredChildren };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.filter(f => f !== null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateViewerPosition() {
|
#updateViewerPosition() {
|
||||||
@ -926,4 +925,27 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
document.getElementById('viewer')?.classList?.add('redaction-viewer');
|
document.getElementById('viewer')?.classList?.add('redaction-viewer');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#createDragOverlay() {
|
||||||
|
if (this.#overlayElement || document.body.contains(this.#overlayElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#overlayElement = document.createElement('div');
|
||||||
|
this.#overlayElement.style.position = 'fixed';
|
||||||
|
this.#overlayElement.style.top = '0';
|
||||||
|
this.#overlayElement.style.left = '0';
|
||||||
|
this.#overlayElement.style.width = '100%';
|
||||||
|
this.#overlayElement.style.height = '100%';
|
||||||
|
this.#overlayElement.style.zIndex = '9999';
|
||||||
|
this.#overlayElement.style.background = 'transparent';
|
||||||
|
this.#overlayElement.style.pointerEvents = 'all';
|
||||||
|
document.body.appendChild(this.#overlayElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
#removeDragOverlay() {
|
||||||
|
if (!this.#overlayElement || !document.body.contains(this.#overlayElement)) return;
|
||||||
|
document.body.removeChild(this.#overlayElement);
|
||||||
|
this.#overlayElement = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { IqserRoutes } from '@iqser/common-ui';
|
import { inject, provideEnvironmentInitializer } from '@angular/core';
|
||||||
import { FilePreviewScreenComponent } from './file-preview-screen.component';
|
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
|
import { IqserRoutes } from '@iqser/common-ui';
|
||||||
|
import { WebSocketService } from '@services/web-socket.service';
|
||||||
|
import { FileAssignService } from '../shared-dossiers/services/file-assign.service';
|
||||||
|
import { FilePreviewScreenComponent } from './file-preview-screen.component';
|
||||||
import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
||||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||||
import { TablesService } from './services/tables.service';
|
import { TablesService } from './services/tables.service';
|
||||||
import { FileAssignService } from '../shared-dossiers/services/file-assign.service';
|
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
@ -13,6 +15,15 @@ export default [
|
|||||||
component: FilePreviewScreenComponent,
|
component: FilePreviewScreenComponent,
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
canDeactivate: [PendingChangesGuard, DocumentUnloadedGuard],
|
canDeactivate: [PendingChangesGuard, DocumentUnloadedGuard],
|
||||||
providers: [FilePreviewDialogService, ManualRedactionService, DocumentUnloadedGuard, TablesService, FileAssignService],
|
providers: [
|
||||||
|
FilePreviewDialogService,
|
||||||
|
ManualRedactionService,
|
||||||
|
DocumentUnloadedGuard,
|
||||||
|
TablesService,
|
||||||
|
FileAssignService,
|
||||||
|
provideEnvironmentInitializer(async () => {
|
||||||
|
return inject(WebSocketService).connectAsync('/redaction-gateway-v1/websocket');
|
||||||
|
}),
|
||||||
|
],
|
||||||
},
|
},
|
||||||
] satisfies IqserRoutes;
|
] satisfies IqserRoutes;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
|
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
|
||||||
import { getConfig } from '@iqser/common-ui';
|
import { getConfig } from '@iqser/common-ui';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List, log } from '@iqser/common-ui/lib/utils';
|
||||||
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 { Core } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
@ -27,6 +27,7 @@ import { EditAnnotationDialogComponent } from '../dialogs/docu-mine/edit-annotat
|
|||||||
import { RemoveAnnotationDialogComponent } from '../dialogs/docu-mine/remove-annotation-dialog/remove-annotation-dialog.component';
|
import { RemoveAnnotationDialogComponent } from '../dialogs/docu-mine/remove-annotation-dialog/remove-annotation-dialog.component';
|
||||||
import { ResizeAnnotationDialogComponent } from '../dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component';
|
import { ResizeAnnotationDialogComponent } from '../dialogs/docu-mine/resize-annotation-dialog/resize-annotation-dialog.component';
|
||||||
import { EditRedactionDialogComponent } from '../dialogs/edit-redaction-dialog/edit-redaction-dialog.component';
|
import { EditRedactionDialogComponent } from '../dialogs/edit-redaction-dialog/edit-redaction-dialog.component';
|
||||||
|
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
|
||||||
import { RedactRecommendationDialogComponent } from '../dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component';
|
import { RedactRecommendationDialogComponent } from '../dialogs/redact-recommendation-dialog/redact-recommendation-dialog.component';
|
||||||
import { RemoveRedactionDialogComponent } from '../dialogs/remove-redaction-dialog/remove-redaction-dialog.component';
|
import { RemoveRedactionDialogComponent } from '../dialogs/remove-redaction-dialog/remove-redaction-dialog.component';
|
||||||
import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dialog/resize-redaction-dialog.component';
|
import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dialog/resize-redaction-dialog.component';
|
||||||
@ -42,6 +43,7 @@ import {
|
|||||||
RemoveRedactionPermissions,
|
RemoveRedactionPermissions,
|
||||||
RemoveRedactionResult,
|
RemoveRedactionResult,
|
||||||
ResizeRedactionData,
|
ResizeRedactionData,
|
||||||
|
RevertManualChangesData,
|
||||||
} from '../utils/dialog-types';
|
} from '../utils/dialog-types';
|
||||||
import { toPosition } from '../utils/pdf-calculation.utils';
|
import { toPosition } from '../utils/pdf-calculation.utils';
|
||||||
import { FileDataService } from './file-data.service';
|
import { FileDataService } from './file-data.service';
|
||||||
@ -49,7 +51,7 @@ import { FilePreviewDialogService } from './file-preview-dialog.service';
|
|||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { ManualRedactionService } from './manual-redaction.service';
|
import { ManualRedactionService } from './manual-redaction.service';
|
||||||
import { SkippedService } from './skipped.service';
|
import { SkippedService } from './skipped.service';
|
||||||
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
|
import { RevertManualChangesDialogComponent } from '../dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnnotationActionsService {
|
export class AnnotationActionsService {
|
||||||
@ -68,6 +70,7 @@ export class AnnotationActionsService {
|
|||||||
private readonly _skippedService: SkippedService,
|
private readonly _skippedService: SkippedService,
|
||||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _permissionsService: PermissionsService,
|
||||||
|
private readonly _iqserDialogService: IqserDialog,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
removeHighlights(highlights: AnnotationWrapper[]): void {
|
removeHighlights(highlights: AnnotationWrapper[]): void {
|
||||||
@ -134,7 +137,11 @@ export class AnnotationActionsService {
|
|||||||
|
|
||||||
let recategorizeBody: List<IRecategorizationRequest> | IBulkRecategorizationRequest;
|
let recategorizeBody: List<IRecategorizationRequest> | IBulkRecategorizationRequest;
|
||||||
|
|
||||||
if (result.option === RedactOrHintOptions.ONLY_HERE || result.option === RectangleRedactOptions.ONLY_THIS_PAGE) {
|
if (
|
||||||
|
result.option === RedactOrHintOptions.ONLY_HERE ||
|
||||||
|
result.option === RectangleRedactOptions.ONLY_THIS_PAGE ||
|
||||||
|
this.#isDocumine
|
||||||
|
) {
|
||||||
recategorizeBody = annotations.map(annotation => {
|
recategorizeBody = annotations.map(annotation => {
|
||||||
const body: IRecategorizationRequest = {
|
const body: IRecategorizationRequest = {
|
||||||
annotationId: annotation.id,
|
annotationId: annotation.id,
|
||||||
@ -164,6 +171,7 @@ export class AnnotationActionsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.pageNumbers = result.pageNumbers || [];
|
||||||
await this.#processObsAndEmit(
|
await this.#processObsAndEmit(
|
||||||
this._manualRedactionService.recategorizeRedactions(
|
this._manualRedactionService.recategorizeRedactions(
|
||||||
recategorizeBody,
|
recategorizeBody,
|
||||||
@ -213,6 +221,22 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async revertChanges(annotations: AnnotationWrapper[]) {
|
||||||
|
const dialogData: RevertManualChangesData = {
|
||||||
|
redactions: annotations,
|
||||||
|
};
|
||||||
|
const result = await this._iqserDialogService.openDefault(RevertManualChangesDialogComponent, { data: dialogData }).result();
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
const data = annotations.map(annotation => annotation.id);
|
||||||
|
const dossierId = this._state.dossierId;
|
||||||
|
const fileId = this._state.fileId;
|
||||||
|
|
||||||
|
this.#processObsAndEmit(
|
||||||
|
this._manualRedactionService.revertChanges(data, dossierId, fileId, this._state.file().excludedFromAutomaticAnalysis),
|
||||||
|
).then();
|
||||||
|
}
|
||||||
|
|
||||||
undoDirectAction(annotations: AnnotationWrapper[]) {
|
undoDirectAction(annotations: AnnotationWrapper[]) {
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
const modifyDictionary = annotations[0].isModifyDictionary;
|
const modifyDictionary = annotations[0].isModifyDictionary;
|
||||||
@ -362,7 +386,7 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async #processObsAndEmit(obs: Observable<unknown>) {
|
async #processObsAndEmit(obs: Observable<unknown>) {
|
||||||
await firstValueFrom(obs).finally(() => this._fileDataService.annotationsChanged());
|
await firstValueFrom(obs.pipe(log('==>>[[[CHANGES]]]'))).finally(() => this._fileDataService.annotationsChanged());
|
||||||
}
|
}
|
||||||
|
|
||||||
#getFalsePositiveText(annotation: AnnotationWrapper) {
|
#getFalsePositiveText(annotation: AnnotationWrapper) {
|
||||||
@ -587,6 +611,7 @@ export class AnnotationActionsService {
|
|||||||
redactions: AnnotationWrapper[],
|
redactions: AnnotationWrapper[],
|
||||||
dialogResult: RemoveRedactionResult,
|
dialogResult: RemoveRedactionResult,
|
||||||
): List<IRemoveRedactionRequest | IBulkLocalRemoveRequest> {
|
): List<IRemoveRedactionRequest | IBulkLocalRemoveRequest> {
|
||||||
|
dialogResult.pageNumbers = dialogResult.pageNumbers || [];
|
||||||
if (dialogResult.bulkLocal || !!dialogResult.pageNumbers.length) {
|
if (dialogResult.bulkLocal || !!dialogResult.pageNumbers.length) {
|
||||||
return dialogResult.positions.map((position, index) => ({
|
return dialogResult.positions.map((position, index) => ({
|
||||||
value: redactions[index].value,
|
value: redactions[index].value,
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { ComponentLogEntry } from '@red/domain';
|
import { ComponentLogEntry } from '@red/domain';
|
||||||
import { INestedFilter, NestedFilter } from '@common-ui/filtering';
|
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@common-ui/filtering';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { sortArray } from '@utils/sorters/custom-sort';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ComponentLogFilterService {
|
export class ComponentLogFilterService {
|
||||||
|
readonly #translateService = inject(TranslateService);
|
||||||
|
|
||||||
filterGroups(entities: ComponentLogEntry[]) {
|
filterGroups(entities: ComponentLogEntry[]) {
|
||||||
const allDistinctComponentLogs = new Set<string>();
|
const allDistinctComponentLogs = new Set<string>();
|
||||||
|
|
||||||
entities?.forEach(entry => allDistinctComponentLogs.add(entry.name));
|
entities?.forEach(entry => allDistinctComponentLogs.add(entry.name));
|
||||||
|
|
||||||
const componentLogFilters = [...allDistinctComponentLogs].map(
|
const componentLogFilters = sortArray([...allDistinctComponentLogs]).map(
|
||||||
id =>
|
id =>
|
||||||
new NestedFilter({
|
new NestedFilter({
|
||||||
id: id,
|
id: id,
|
||||||
@ -21,7 +25,9 @@ export class ComponentLogFilterService {
|
|||||||
{
|
{
|
||||||
slug: 'componentLogFilters',
|
slug: 'componentLogFilters',
|
||||||
filters: componentLogFilters,
|
filters: componentLogFilters,
|
||||||
},
|
checker: keyChecker('name'),
|
||||||
|
filterceptionPlaceholder: this.#translateService.instant('component-management.filter.search-placeholder'),
|
||||||
|
} as IFilterGroup,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
|
import { effect, inject, Injectable, Signal, signal, WritableSignal } from '@angular/core';
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { TenantsService } from '@common-ui/tenants';
|
||||||
|
import { log } from '@common-ui/utils';
|
||||||
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import {
|
import {
|
||||||
@ -27,6 +29,7 @@ import { UserPreferenceService } from '@users/user-preference.service';
|
|||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
|
import { switchMap, tap } from 'rxjs/operators';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { MultiSelectService } from './multi-select.service';
|
import { MultiSelectService } from './multi-select.service';
|
||||||
import { ViewModeService } from './view-mode.service';
|
import { ViewModeService } from './view-mode.service';
|
||||||
@ -43,13 +46,14 @@ export function chronologicallyBy<T>(property: (x: T) => string) {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
|
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
|
||||||
readonly #annotations = signal<AnnotationWrapper[]>([]);
|
readonly #annotations: WritableSignal<AnnotationWrapper[]>;
|
||||||
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
|
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
|
||||||
#originalViewedPages: ViewedPage[] = [];
|
#originalViewedPages: ViewedPage[] = [];
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly #logger = inject(NGXLogger);
|
readonly #logger = inject(NGXLogger);
|
||||||
readonly #toaster = inject(Toaster);
|
readonly #toaster = inject(Toaster);
|
||||||
readonly #isIqserDevMode = inject(UserPreferenceService).isIqserDevMode;
|
readonly #isIqserDevMode = inject(UserPreferenceService).isIqserDevMode;
|
||||||
|
readonly #tenantsService = inject(TenantsService);
|
||||||
protected readonly _entityClass = AnnotationWrapper;
|
protected readonly _entityClass = AnnotationWrapper;
|
||||||
missingTypes = new Set<string>();
|
missingTypes = new Set<string>();
|
||||||
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
|
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
|
||||||
@ -70,9 +74,24 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
private readonly _defaultColorsService: DefaultColorsService,
|
private readonly _defaultColorsService: DefaultColorsService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
const localStorageKey = this.#tenantsService.activeTenantId + '-annotations-' + this._state.fileId;
|
||||||
|
const storedAnnotations = JSON.parse(localStorage.getItem(localStorageKey) || '[]') as [];
|
||||||
|
this.#annotations = signal<AnnotationWrapper[]>(
|
||||||
|
storedAnnotations.map(a => {
|
||||||
|
const newAnn = new AnnotationWrapper();
|
||||||
|
Object.assign(newAnn, a);
|
||||||
|
return newAnn;
|
||||||
|
}),
|
||||||
|
);
|
||||||
this.annotations$ = toObservable(this.#annotations);
|
this.annotations$ = toObservable(this.#annotations);
|
||||||
this.annotations = this.#annotations.asReadonly();
|
this.annotations = this.#annotations.asReadonly();
|
||||||
this.earmarks = this.#earmarks.asReadonly();
|
this.earmarks = this.#earmarks.asReadonly();
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
localStorage.setItem(localStorageKey, JSON.stringify(this.#annotations()));
|
||||||
|
console.log('FileDataService#annotations', this.#annotations());
|
||||||
|
});
|
||||||
|
|
||||||
effect(() => {
|
effect(() => {
|
||||||
const viewMode = this._viewModeService.viewMode();
|
const viewMode = this._viewModeService.viewMode();
|
||||||
const earmarks = ([] as AnnotationWrapper[]).concat(...this.#earmarks().values());
|
const earmarks = ([] as AnnotationWrapper[]).concat(...this.#earmarks().values());
|
||||||
@ -119,7 +138,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
const file = this._state.file();
|
const file = this._state.file();
|
||||||
const fileReloaded = await this._filesService.reload(file.dossierId, file);
|
const fileReloaded = await this._filesService.reload(file.dossierId, file);
|
||||||
if (!fileReloaded) {
|
if (!fileReloaded) {
|
||||||
await this.loadAnnotations(file);
|
await this.#loadViewedPages(file);
|
||||||
|
// await this.loadAnnotations(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +162,31 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
this.#annotations.set(annotations);
|
this.#annotations.set(annotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async processEntityLog(entityLog: IEntityLog) {
|
||||||
|
let annotations = await this.#convertData(entityLog);
|
||||||
|
this.#checkMissingTypes();
|
||||||
|
|
||||||
|
return this.#isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnnotations(file: File, analysisNumber: number) {
|
||||||
|
const delta$ = this._entityLogService.getDelta(file.dossierId, file.fileId, analysisNumber);
|
||||||
|
return delta$.pipe(
|
||||||
|
log('[REDACTION_LOG] Delta loaded'),
|
||||||
|
switchMap(delta => this.processEntityLog(delta)),
|
||||||
|
tap(annotations => {
|
||||||
|
const notDeleted = annotations.filter(annotation => !annotation.isRemoved);
|
||||||
|
this.#annotations.update(old => {
|
||||||
|
const notUpdated = old.filter(oldAnnotation => {
|
||||||
|
return !oldAnnotation.pending && !annotations.some(newAnnotation => newAnnotation.id === oldAnnotation.id);
|
||||||
|
});
|
||||||
|
return [...notUpdated, ...notDeleted].sort((a, b) => a.positions[0].page - b.positions[0].page);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
tap(() => this.#logger.info('[REDACTION_LOG] Annotations updated', this.#annotations())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#checkMissingTypes() {
|
#checkMissingTypes() {
|
||||||
if (this.missingTypes.size > 0) {
|
if (this.missingTypes.size > 0) {
|
||||||
this.#toaster.error(_('error.missing-types'), {
|
this.#toaster.error(_('error.missing-types'), {
|
||||||
@ -190,7 +235,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canBeMappedToASuperType = !!SuperTypeMapper[entry.entryType][entry.state](entry);
|
const isRemoved = entry.state === EntryStates.REMOVED;
|
||||||
|
const canBeMappedToASuperType = !!SuperTypeMapper[entry.entryType][entry.state](entry) || isRemoved;
|
||||||
if (!canBeMappedToASuperType) {
|
if (!canBeMappedToASuperType) {
|
||||||
if (this.#isIqserDevMode) {
|
if (this.#isIqserDevMode) {
|
||||||
this.#logger.warn(
|
this.#logger.warn(
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { GenericService, IqserPermissionsService, Toaster } from '@iqser/common-ui';
|
import { GenericService, IqserPermissionsService, QueryParam, Toaster } from '@iqser/common-ui';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
|
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
|
||||||
@ -108,6 +108,10 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
|
return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revertChanges(body: List<string>, dossierId: string, fileId: string, includeOnlyUnprocessed: boolean) {
|
||||||
|
return this.#revertChanges(body, dossierId, fileId, includeOnlyUnprocessed).pipe(this.#showToast('revert-changes'));
|
||||||
|
}
|
||||||
|
|
||||||
removeRedaction(
|
removeRedaction(
|
||||||
body: List<IRemoveRedactionRequest> | IBulkLocalRemoveRequest,
|
body: List<IRemoveRedactionRequest> | IBulkLocalRemoveRequest,
|
||||||
dossierId: string,
|
dossierId: string,
|
||||||
@ -154,6 +158,14 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}`).pipe(this.#log('Resize', body));
|
return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}`).pipe(this.#log('Resize', body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#revertChanges(body: List<string>, dossierId: string, fileId: string, includeOnlyUnprocessed: boolean) {
|
||||||
|
const queryParams: List<QueryParam> = [
|
||||||
|
{ key: 'includeOnlyUnprocessed', value: includeOnlyUnprocessed },
|
||||||
|
{ key: 'includeOnlyLocal', value: true },
|
||||||
|
];
|
||||||
|
return this.delete(body, `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`, queryParams);
|
||||||
|
}
|
||||||
|
|
||||||
#recategorize(
|
#recategorize(
|
||||||
body: List<IRecategorizationRequest> | IBulkRecategorizationRequest,
|
body: List<IRecategorizationRequest> | IBulkRecategorizationRequest,
|
||||||
dossierId: string,
|
dossierId: string,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manag
|
|||||||
import { AnnotationActionsService } from './annotation-actions.service';
|
import { AnnotationActionsService } from './annotation-actions.service';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { UI_ROOT_PATH_FN } from '@common-ui/utils';
|
import { UI_ROOT_PATH_FN } from '@common-ui/utils';
|
||||||
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PdfAnnotationActionsService {
|
export class PdfAnnotationActionsService {
|
||||||
@ -20,8 +21,10 @@ export class PdfAnnotationActionsService {
|
|||||||
readonly #annotationActionsService = inject(AnnotationActionsService);
|
readonly #annotationActionsService = inject(AnnotationActionsService);
|
||||||
readonly #iqserPermissionsService = inject(IqserPermissionsService);
|
readonly #iqserPermissionsService = inject(IqserPermissionsService);
|
||||||
readonly #annotationManager = inject(REDAnnotationManager);
|
readonly #annotationManager = inject(REDAnnotationManager);
|
||||||
|
readonly #userPreferences = inject(UserPreferenceService);
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly #convertPath = inject(UI_ROOT_PATH_FN);
|
readonly #convertPath = inject(UI_ROOT_PATH_FN);
|
||||||
|
readonly #devMode = this.#userPreferences.isIqserDevMode;
|
||||||
|
|
||||||
get(annotations: AnnotationWrapper[], annotationChangesAllowed: boolean): IHeaderElement[] {
|
get(annotations: AnnotationWrapper[], annotationChangesAllowed: boolean): IHeaderElement[] {
|
||||||
const availableActions: IHeaderElement[] = [];
|
const availableActions: IHeaderElement[] = [];
|
||||||
@ -97,6 +100,13 @@ export class PdfAnnotationActionsService {
|
|||||||
availableActions.push(forceHintButton);
|
availableActions.push(forceHintButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (permissions.canRevertChanges && annotationChangesAllowed && this.#devMode) {
|
||||||
|
const revertChangesButton = this.#getButton('general/revert-changes', _('annotation-actions.revert-changes.label'), () =>
|
||||||
|
this.#annotationActionsService.revertChanges(annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(revertChangesButton);
|
||||||
|
}
|
||||||
|
|
||||||
if (permissions.canRemoveRedaction && sameType && annotationChangesAllowed) {
|
if (permissions.canRemoveRedaction && sameType && annotationChangesAllowed) {
|
||||||
const removeRedactionButton = this.#getButton('trash', _('annotation-actions.remove-annotation.remove-redaction'), () =>
|
const removeRedactionButton = this.#getButton('trash', _('annotation-actions.remove-annotation.remove-redaction'), () =>
|
||||||
this.#annotationActionsService.removeRedaction(annotations, permissions),
|
this.#annotationActionsService.removeRedaction(annotations, permissions),
|
||||||
|
|||||||
@ -183,3 +183,7 @@ export interface RectangleDialogData {
|
|||||||
export interface RectangleDialogResult {
|
export interface RectangleDialogResult {
|
||||||
annotation: IManualRedactionEntry;
|
annotation: IManualRedactionEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RevertManualChangesData {
|
||||||
|
redactions: AnnotationWrapper[];
|
||||||
|
}
|
||||||
|
|||||||
@ -75,6 +75,7 @@ export class IconsModule {
|
|||||||
'reference',
|
'reference',
|
||||||
'remove-from-dict',
|
'remove-from-dict',
|
||||||
'report',
|
'report',
|
||||||
|
'revert-changes',
|
||||||
'rotation',
|
'rotation',
|
||||||
'rss',
|
'rss',
|
||||||
'rule',
|
'rule',
|
||||||
|
|||||||
@ -210,6 +210,7 @@ export class REDAnnotationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#getByIds(annotations: List | List<AnnotationWrapper>) {
|
#getByIds(annotations: List | List<AnnotationWrapper>) {
|
||||||
|
annotations = annotations ?? [];
|
||||||
return annotations.map((item: string | AnnotationWrapper) => this.#getById(item)).filter(a => !!a);
|
return annotations.map((item: string | AnnotationWrapper) => this.#getById(item)).filter(a => !!a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
import { Component, computed, HostBinding, Injector, input, Optional, signal, ViewChild } from '@angular/core';
|
import { NgIf, NgTemplateOutlet } from '@angular/common';
|
||||||
|
import { Component, computed, HostBinding, inject, Injector, input, Optional, signal, ViewChild } from '@angular/core';
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { StatusBarComponent } from '@common-ui/shared';
|
||||||
import {
|
import {
|
||||||
CircleButtonTypes,
|
CircleButtonTypes,
|
||||||
getConfig,
|
getConfig,
|
||||||
@ -20,24 +22,23 @@ import { FileManagementService } from '@services/files/file-management.service';
|
|||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { ReanalysisService, ReanalyzeQueryParams } from '@services/reanalysis.service';
|
import { ReanalysisService, ReanalyzeQueryParams } from '@services/reanalysis.service';
|
||||||
|
import { ApproveWarningDetailsComponent } from '@shared/components/approve-warning-details/approve-warning-details.component';
|
||||||
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
|
||||||
|
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
|
||||||
import { LongPressDirective, LongPressEvent } from '@shared/directives/long-press.directive';
|
import { LongPressDirective, LongPressEvent } from '@shared/directives/long-press.directive';
|
||||||
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 { setLocalStorageDataByFileId } from '@utils/local-storage';
|
import { setLocalStorageDataByFileId } from '@utils/local-storage';
|
||||||
import { firstValueFrom, Observable } from 'rxjs';
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
|
import { RulesService } from '../../../admin/services/rules.service';
|
||||||
import { DocumentInfoService } from '../../../file-preview/services/document-info.service';
|
import { DocumentInfoService } from '../../../file-preview/services/document-info.service';
|
||||||
import { ExcludedPagesService } from '../../../file-preview/services/excluded-pages.service';
|
import { ExcludedPagesService } from '../../../file-preview/services/excluded-pages.service';
|
||||||
|
import { FileDataService } from '../../../file-preview/services/file-data.service';
|
||||||
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
||||||
import { ViewerHeaderService } from '../../../pdf-viewer/services/viewer-header.service';
|
import { ViewerHeaderService } from '../../../pdf-viewer/services/viewer-header.service';
|
||||||
import { AssignReviewerApproverDialogComponent } from '../../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
import { AssignReviewerApproverDialogComponent } from '../../dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||||
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
|
||||||
import { FileAssignService } from '../../services/file-assign.service';
|
import { FileAssignService } from '../../services/file-assign.service';
|
||||||
import { ProcessingIndicatorComponent } from '@shared/components/processing-indicator/processing-indicator.component';
|
|
||||||
import { StatusBarComponent } from '@common-ui/shared';
|
|
||||||
import { NgIf, NgTemplateOutlet } from '@angular/common';
|
|
||||||
import { ApproveWarningDetailsComponent } from '@shared/components/approve-warning-details/approve-warning-details.component';
|
|
||||||
import { RulesService } from '../../../admin/services/rules.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-file-actions',
|
selector: 'redaction-file-actions',
|
||||||
@ -48,8 +49,49 @@ import { RulesService } from '../../../admin/services/rules.service';
|
|||||||
export class FileActionsComponent {
|
export class FileActionsComponent {
|
||||||
@ViewChild(ExpandableFileActionsComponent)
|
@ViewChild(ExpandableFileActionsComponent)
|
||||||
private readonly _expandableActionsComponent: ExpandableFileActionsComponent;
|
private readonly _expandableActionsComponent: ExpandableFileActionsComponent;
|
||||||
|
readonly #fileDataService = inject(FileDataService, { optional: true });
|
||||||
|
readonly #analysisForced = signal(false);
|
||||||
|
readonly #ariaExpanded$ = toObservable(this._documentInfoService?.shown);
|
||||||
|
readonly #areRulesLocked = computed(() => this._rulesService.currentTemplateRules().timeoutDetected);
|
||||||
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
readonly file = input.required<File>();
|
readonly file = input.required<File>();
|
||||||
|
readonly #assignTooltip? = computed(() =>
|
||||||
|
this.file().isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'),
|
||||||
|
);
|
||||||
|
readonly #showReanalyse = computed(
|
||||||
|
() => (this.#canReanalyse() || this.file().excludedFromAutomaticAnalysis || this.#analysisForced()) && !this.file().dossierArchived,
|
||||||
|
);
|
||||||
|
readonly #toggleTooltip? = computed(() => {
|
||||||
|
if (!this.#canToggleAnalysis()) {
|
||||||
|
return _('file-preview.toggle-analysis.only-managers');
|
||||||
|
}
|
||||||
|
return this.file()?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
||||||
|
});
|
||||||
|
readonly #showDownload = computed(
|
||||||
|
() =>
|
||||||
|
this._permissionsService.canDownloadRedactedFile() &&
|
||||||
|
!!this.file().lastProcessed &&
|
||||||
|
!this.file().isError &&
|
||||||
|
this.#isDossierMember(),
|
||||||
|
);
|
||||||
readonly dossier = input.required<Dossier>();
|
readonly dossier = input.required<Dossier>();
|
||||||
|
readonly #showImportRedactions = computed(
|
||||||
|
() => this._permissionsService.canImportRedactions(this.file(), this.dossier()) && !this.file().isError,
|
||||||
|
);
|
||||||
|
readonly #showDelete = computed(() => this._permissionsService.canSoftDeleteFile(this.file(), this.dossier()));
|
||||||
|
readonly #showOCR = computed(() => this._permissionsService.canOcrFile(this.file(), this.dossier()) && !this.file().isError);
|
||||||
|
readonly #canReanalyse = computed(() => this._permissionsService.canReanalyseFile(this.file(), this.dossier()));
|
||||||
|
readonly #canEnableAutoAnalysis = computed(
|
||||||
|
() => this._permissionsService.canEnableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
|
||||||
|
);
|
||||||
|
readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.file(), this.dossier()));
|
||||||
|
readonly #showToggleAnalysis = computed(
|
||||||
|
() => !!this.file().lastProcessed && this._permissionsService.showToggleAnalysis(this.dossier()),
|
||||||
|
);
|
||||||
|
readonly #isDossierMember = computed(() => this._permissionsService.isDossierMember(this.dossier()));
|
||||||
|
readonly #canDisableAutoAnalysis = computed(
|
||||||
|
() => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
|
||||||
|
);
|
||||||
readonly type = input.required<'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow'>();
|
readonly type = input.required<'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow'>();
|
||||||
readonly maxWidth = input<number>();
|
readonly maxWidth = input<number>();
|
||||||
readonly minWidth = input<number>();
|
readonly minWidth = input<number>();
|
||||||
@ -57,16 +99,21 @@ export class FileActionsComponent {
|
|||||||
readonly singleEntityAction = input(false);
|
readonly singleEntityAction = input(false);
|
||||||
readonly currentUser = getCurrentUser<User>();
|
readonly currentUser = getCurrentUser<User>();
|
||||||
readonly tooltipPosition = IqserTooltipPositions.above;
|
readonly tooltipPosition = IqserTooltipPositions.above;
|
||||||
|
|
||||||
readonly isDossierOverview = computed(() => this.type().startsWith('dossier-overview'));
|
readonly isDossierOverview = computed(() => this.type().startsWith('dossier-overview'));
|
||||||
|
readonly #showAssignToSelf = computed(
|
||||||
|
() => this._permissionsService.canAssignToSelf(this.file(), this.dossier()) && this.isDossierOverview(),
|
||||||
|
);
|
||||||
|
readonly #showAssign = computed(
|
||||||
|
() =>
|
||||||
|
(this._permissionsService.canAssignUser(this.file(), this.dossier()) ||
|
||||||
|
this._permissionsService.canUnassignUser(this.file(), this.dossier())) &&
|
||||||
|
this.isDossierOverview(),
|
||||||
|
);
|
||||||
|
readonly #showReanalyseDossierOverview = computed(
|
||||||
|
() => this.#showReanalyse() && this.isDossierOverview() && !this.file().isApproved && this.#isDossierMember(),
|
||||||
|
);
|
||||||
readonly isDossierOverviewList = computed(() => this.type() === 'dossier-overview-list');
|
readonly isDossierOverviewList = computed(() => this.type() === 'dossier-overview-list');
|
||||||
readonly isDossierOverviewWorkflow = computed(() => this.type() === 'dossier-overview-workflow');
|
readonly isDossierOverviewWorkflow = computed(() => this.type() === 'dossier-overview-workflow');
|
||||||
readonly isFilePreview = computed(() => this.type() === 'file-preview');
|
|
||||||
readonly buttons = computed(() => this.#buttons);
|
|
||||||
readonly showStatusBar = computed(() => !this.file().isError && !this.file().isUnprocessed && this.isDossierOverviewList());
|
|
||||||
readonly #assignTooltip? = computed(() =>
|
|
||||||
this.file().isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'),
|
|
||||||
);
|
|
||||||
readonly #showSetToNew = computed(
|
readonly #showSetToNew = computed(
|
||||||
() =>
|
() =>
|
||||||
this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow() && !this.file().isError,
|
this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow() && !this.file().isError,
|
||||||
@ -77,24 +124,6 @@ export class FileActionsComponent {
|
|||||||
!this.isDossierOverviewWorkflow() &&
|
!this.isDossierOverviewWorkflow() &&
|
||||||
!this.file().isError,
|
!this.file().isError,
|
||||||
);
|
);
|
||||||
readonly #showAssignToSelf = computed(
|
|
||||||
() => this._permissionsService.canAssignToSelf(this.file(), this.dossier()) && this.isDossierOverview(),
|
|
||||||
);
|
|
||||||
readonly #showImportRedactions = computed(
|
|
||||||
() => this._permissionsService.canImportRedactions(this.file(), this.dossier()) && !this.file().isError,
|
|
||||||
);
|
|
||||||
readonly #showAssign = computed(
|
|
||||||
() =>
|
|
||||||
(this._permissionsService.canAssignUser(this.file(), this.dossier()) ||
|
|
||||||
this._permissionsService.canUnassignUser(this.file(), this.dossier())) &&
|
|
||||||
this.isDossierOverview(),
|
|
||||||
);
|
|
||||||
readonly #showDelete = computed(() => this._permissionsService.canSoftDeleteFile(this.file(), this.dossier()));
|
|
||||||
readonly #showOCR = computed(() => this._permissionsService.canOcrFile(this.file(), this.dossier()) && !this.file().isError);
|
|
||||||
readonly #canReanalyse = computed(() => this._permissionsService.canReanalyseFile(this.file(), this.dossier()));
|
|
||||||
readonly #canEnableAutoAnalysis = computed(
|
|
||||||
() => this._permissionsService.canEnableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
|
|
||||||
);
|
|
||||||
readonly #showUnderReview = computed(
|
readonly #showUnderReview = computed(
|
||||||
() =>
|
() =>
|
||||||
this._permissionsService.canSetUnderReview(this.file(), this.dossier()) &&
|
this._permissionsService.canSetUnderReview(this.file(), this.dossier()) &&
|
||||||
@ -113,41 +142,12 @@ export class FileActionsComponent {
|
|||||||
!this.isDossierOverviewWorkflow() &&
|
!this.isDossierOverviewWorkflow() &&
|
||||||
!this.file().isError,
|
!this.file().isError,
|
||||||
);
|
);
|
||||||
readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.file(), this.dossier()));
|
readonly isFilePreview = computed(() => this.type() === 'file-preview');
|
||||||
readonly #toggleTooltip? = computed(() => {
|
|
||||||
if (!this.#canToggleAnalysis()) {
|
|
||||||
return _('file-preview.toggle-analysis.only-managers');
|
|
||||||
}
|
|
||||||
return this.file()?.excluded ? _('file-preview.toggle-analysis.enable') : _('file-preview.toggle-analysis.disable');
|
|
||||||
});
|
|
||||||
readonly #showToggleAnalysis = computed(
|
|
||||||
() => !!this.file().lastProcessed && this._permissionsService.showToggleAnalysis(this.dossier()),
|
|
||||||
);
|
|
||||||
readonly #analysisForced = signal(false);
|
|
||||||
readonly #showReanalyse = computed(
|
|
||||||
() => (this.#canReanalyse() || this.file().excludedFromAutomaticAnalysis || this.#analysisForced()) && !this.file().dossierArchived,
|
|
||||||
);
|
|
||||||
readonly #isDossierMember = computed(() => this._permissionsService.isDossierMember(this.dossier()));
|
|
||||||
readonly #showDownload = computed(
|
|
||||||
() =>
|
|
||||||
this._permissionsService.canDownloadRedactedFile() &&
|
|
||||||
!!this.file().lastProcessed &&
|
|
||||||
!this.file().isError &&
|
|
||||||
this.#isDossierMember(),
|
|
||||||
);
|
|
||||||
readonly #showReanalyseFilePreview = computed(
|
readonly #showReanalyseFilePreview = computed(
|
||||||
() => this.#showReanalyse() && this.isFilePreview() && !this.file().isApproved && this.#isDossierMember(),
|
() => this.#showReanalyse() && this.isFilePreview() && !this.file().isApproved && this.#isDossierMember(),
|
||||||
);
|
);
|
||||||
readonly #showReanalyseDossierOverview = computed(
|
readonly buttons = computed(() => this.#buttons);
|
||||||
() => this.#showReanalyse() && this.isDossierOverview() && !this.file().isApproved && this.#isDossierMember(),
|
readonly showStatusBar = computed(() => !this.file().isError && !this.file().isUnprocessed && this.isDossierOverviewList());
|
||||||
);
|
|
||||||
readonly #ariaExpanded$ = toObservable(this._documentInfoService?.shown);
|
|
||||||
readonly #areRulesLocked = computed(() => this._rulesService.currentTemplateRules().timeoutDetected);
|
|
||||||
|
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
readonly #canDisableAutoAnalysis = computed(
|
|
||||||
() => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
|
|
||||||
);
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _injector: Injector,
|
private readonly _injector: Injector,
|
||||||
@ -296,7 +296,7 @@ export class FileActionsComponent {
|
|||||||
icon: 'iqser:refresh',
|
icon: 'iqser:refresh',
|
||||||
show: this.#showReanalyseFilePreview(),
|
show: this.#showReanalyseFilePreview(),
|
||||||
disabled: this.file().isProcessing || this.#areRulesLocked(),
|
disabled: this.file().isProcessing || this.#areRulesLocked(),
|
||||||
helpModeKey: 'stop_analysis',
|
helpModeKey: this.#isDocumine ? 'analyze_file' : 'stop_analysis',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'btn-toggle_automatic_analysis',
|
id: 'btn-toggle_automatic_analysis',
|
||||||
@ -334,7 +334,7 @@ export class FileActionsComponent {
|
|||||||
icon: 'iqser:refresh',
|
icon: 'iqser:refresh',
|
||||||
show: this.#showReanalyseDossierOverview(),
|
show: this.#showReanalyseDossierOverview(),
|
||||||
disabled: this.#areRulesLocked(),
|
disabled: this.#areRulesLocked(),
|
||||||
helpModeKey: 'stop_analysis',
|
helpModeKey: this.#isDocumine ? 'analyze_file' : 'stop_analysis',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'btn-toggle_analysis',
|
id: 'btn-toggle_analysis',
|
||||||
@ -433,6 +433,9 @@ export class FileActionsComponent {
|
|||||||
|
|
||||||
async #assignToMe() {
|
async #assignToMe() {
|
||||||
await this._fileAssignService.assignToMe([this.file()]);
|
await this._fileAssignService.assignToMe([this.file()]);
|
||||||
|
// TODO: check which one to call
|
||||||
|
// await firstValueFrom(this.#fileDataService?.updateAnnotations(this.file, this.file.numberOfAnalyses));
|
||||||
|
await this.#fileDataService?.loadEntityLog();
|
||||||
}
|
}
|
||||||
|
|
||||||
async #reanalyseFile() {
|
async #reanalyseFile() {
|
||||||
|
|||||||
@ -1,14 +1,6 @@
|
|||||||
import { Component, HostListener } from '@angular/core';
|
import { Component, HostListener } from '@angular/core';
|
||||||
import {
|
import { CircleButtonComponent, IconButtonComponent, IqserDialogComponent, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
CircleButtonComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
IconButtonTypes,
|
|
||||||
IqserDialogComponent,
|
|
||||||
LoadingService,
|
|
||||||
Toaster,
|
|
||||||
} from '@iqser/common-ui';
|
|
||||||
import { MatDialogClose } from '@angular/material/dialog';
|
import { MatDialogClose } from '@angular/material/dialog';
|
||||||
import { MatFormField } from '@angular/material/form-field';
|
|
||||||
import { NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
@ -31,16 +23,7 @@ interface ReturnType {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-edit-dictionary-dialog',
|
selector: 'redaction-edit-dictionary-dialog',
|
||||||
imports: [
|
imports: [CircleButtonComponent, IconButtonComponent, MatDialogClose, ReactiveFormsModule, TranslateModule, MatCheckbox, NgIf],
|
||||||
CircleButtonComponent,
|
|
||||||
IconButtonComponent,
|
|
||||||
MatDialogClose,
|
|
||||||
MatFormField,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
TranslateModule,
|
|
||||||
MatCheckbox,
|
|
||||||
NgIf,
|
|
||||||
],
|
|
||||||
templateUrl: './edit-dictionary-dialog.component.html',
|
templateUrl: './edit-dictionary-dialog.component.html',
|
||||||
})
|
})
|
||||||
export class EditDictionaryDialogComponent extends IqserDialogComponent<EditDictionaryDialogComponent, DialogData, ReturnType> {
|
export class EditDictionaryDialogComponent extends IqserDialogComponent<EditDictionaryDialogComponent, DialogData, ReturnType> {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { MatDatepickerModule } from '@angular/material/datepicker';
|
|||||||
import { MatDialogRef } from '@angular/material/dialog';
|
import { MatDialogRef } from '@angular/material/dialog';
|
||||||
import { MatFormField, MatSuffix } from '@angular/material/form-field';
|
import { MatFormField, MatSuffix } from '@angular/material/form-field';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
|
import { MatOption, MatSelect } from '@angular/material/select';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
@ -68,7 +68,6 @@ interface GeneralInfoForm {
|
|||||||
MatSuffix,
|
MatSuffix,
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
NgIf,
|
NgIf,
|
||||||
MatSelectTrigger,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSectionInterface {
|
export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSectionInterface {
|
||||||
|
|||||||
@ -1,23 +1,10 @@
|
|||||||
import { Component, computed, input } from '@angular/core';
|
import { Component, computed, input } from '@angular/core';
|
||||||
import { ApproveResponse, File } from '@red/domain';
|
import { ApproveResponse, File } from '@red/domain';
|
||||||
import { SelectedAnnotationsTableComponent } from '../../../file-preview/components/selected-annotations-table/selected-annotations-table.component';
|
|
||||||
import { MatExpansionPanel, MatExpansionPanelHeader } from '@angular/material/expansion';
|
|
||||||
import { KeyValuePipe, NgStyle } from '@angular/common';
|
|
||||||
import { WarningDetailsPanelComponent } from '@shared/components/warning-details-panel/warning-details-panel.component';
|
import { WarningDetailsPanelComponent } from '@shared/components/warning-details-panel/warning-details-panel.component';
|
||||||
import { CdkFixedSizeVirtualScroll, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-approve-warning-details',
|
selector: 'redaction-approve-warning-details',
|
||||||
imports: [
|
imports: [WarningDetailsPanelComponent],
|
||||||
SelectedAnnotationsTableComponent,
|
|
||||||
MatExpansionPanel,
|
|
||||||
MatExpansionPanelHeader,
|
|
||||||
KeyValuePipe,
|
|
||||||
WarningDetailsPanelComponent,
|
|
||||||
CdkVirtualScrollViewport,
|
|
||||||
CdkFixedSizeVirtualScroll,
|
|
||||||
NgStyle,
|
|
||||||
],
|
|
||||||
templateUrl: './approve-warning-details.component.html',
|
templateUrl: './approve-warning-details.component.html',
|
||||||
styleUrl: './approve-warning-details.component.scss',
|
styleUrl: './approve-warning-details.component.scss',
|
||||||
})
|
})
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import { saveAs } from 'file-saver';
|
|||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { MatIcon } from '@angular/material/icon';
|
import { MatIcon } from '@angular/material/icon';
|
||||||
import { NgForOf, NgIf } from '@angular/common';
|
import { NgIf } from '@angular/common';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { MatTooltip } from '@angular/material/tooltip';
|
import { MatTooltip } from '@angular/material/tooltip';
|
||||||
@ -55,7 +55,6 @@ const HELP_MODE_KEYS = {
|
|||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
MatDivider,
|
MatDivider,
|
||||||
NgForOf,
|
|
||||||
IconButtonComponent,
|
IconButtonComponent,
|
||||||
EditorComponent,
|
EditorComponent,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
import { ApplicationRef, Injectable, OnDestroy } from '@angular/core';
|
|
||||||
import { FileUploadModel } from '../model/file-upload.model';
|
|
||||||
import { HttpErrorResponse, HttpEventType, HttpStatusCode } from '@angular/common/http';
|
import { HttpErrorResponse, HttpEventType, HttpStatusCode } from '@angular/common/http';
|
||||||
import { interval, Subject, Subscription } from 'rxjs';
|
import { ApplicationRef, Injectable, OnDestroy } from '@angular/core';
|
||||||
import { ConfigService } from '@services/config.service';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { TenantsService } from '@common-ui/tenants';
|
||||||
|
import { ErrorMessageService, GenericService, Toaster } from '@iqser/common-ui';
|
||||||
|
import { HeadersConfiguration } from '@iqser/common-ui/lib/utils';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { IFileUploadResult, OverwriteFileOption, OverwriteFileOptions } from '@red/domain';
|
import { IFileUploadResult, OverwriteFileOption, OverwriteFileOptions } from '@red/domain';
|
||||||
import { isAcceptedFileType, isCsv, isDocument, isZip } from '@utils/file-drop-utils';
|
import { ConfigService } from '@services/config.service';
|
||||||
import { ErrorMessageService, GenericService, Toaster } from '@iqser/common-ui';
|
|
||||||
import { FilesMapService } from '@services/files/files-map.service';
|
import { FilesMapService } from '@services/files/files-map.service';
|
||||||
import { switchMap, tap, throttleTime } from 'rxjs/operators';
|
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { UploadDownloadDialogService } from './upload-download-dialog.service';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { HeadersConfiguration } from '@iqser/common-ui/lib/utils';
|
|
||||||
import { LicenseService } from '@services/license.service';
|
import { LicenseService } from '@services/license.service';
|
||||||
import { LicenseFeatures } from '../../admin/screens/license/utils/constants';
|
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
|
import { isAcceptedFileType, isCsv, isZip } from '@utils/file-drop-utils';
|
||||||
|
import { interval, Subject, Subscription } from 'rxjs';
|
||||||
|
import { switchMap, tap, throttleTime } from 'rxjs/operators';
|
||||||
|
import { LicenseFeatures } from '../../admin/screens/license/utils/constants';
|
||||||
|
import { FileUploadModel } from '../model/file-upload.model';
|
||||||
|
import { UploadDownloadDialogService } from './upload-download-dialog.service';
|
||||||
|
|
||||||
export interface ActiveUpload {
|
export interface ActiveUpload {
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
@ -45,6 +46,7 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
|||||||
private readonly _licenseService: LicenseService,
|
private readonly _licenseService: LicenseService,
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
|
private readonly _tenantsService: TenantsService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
const fileFetch$ = this.#fetchFiles$.pipe(
|
const fileFetch$ = this.#fetchFiles$.pipe(
|
||||||
@ -111,6 +113,11 @@ export class FileUploadService extends GenericService<IFileUploadResult> impleme
|
|||||||
option = res.applyToAllFiles ? currentOption : undefined;
|
option = res.applyToAllFiles ? currentOption : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingFile = dossierFiles.find(pf => pf.filename === file.file.name);
|
||||||
|
if (OverwriteFileOptions.FULL_OVERWRITE === currentOption || OverwriteFileOptions.PARTIAL_OVERWRITE === currentOption) {
|
||||||
|
localStorage.removeItem(`${this._tenantsService.activeTenantId}-annotations-${existingFile.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (currentOption === OverwriteFileOptions.PARTIAL_OVERWRITE) {
|
if (currentOption === OverwriteFileOptions.PARTIAL_OVERWRITE) {
|
||||||
file.keepManualRedactions = true;
|
file.keepManualRedactions = true;
|
||||||
}
|
}
|
||||||
|
|||||||
18
apps/red-ui/src/app/services/copilot.service.ts
Normal file
18
apps/red-ui/src/app/services/copilot.service.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { StompService } from '@services/stomp.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class CopilotService extends StompService {
|
||||||
|
override get topicPrefix(): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
override send(message: string) {
|
||||||
|
this.publish({
|
||||||
|
destination: '/app/rules-copilot',
|
||||||
|
body: JSON.stringify({ prompts: [message] }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { StatsService } from '@iqser/common-ui';
|
import { StatsService } from '@iqser/common-ui';
|
||||||
import { DashboardStats, DOSSIER_ID, DossierStats, IDossierStats } from '@red/domain';
|
import { DOSSIER_ID, DossierStats, IDossierStats } from '@red/domain';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|||||||
@ -1,34 +1,49 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { GenericService, Toaster } from '@iqser/common-ui';
|
import { GenericService, Toaster } from '@iqser/common-ui';
|
||||||
import { EntryStates, IEntityLog, IEntityLogEntry } from '@red/domain';
|
import { EntryState, EntryStates, IEntityLog, IEntityLogEntry, ISectionGrid } from '@red/domain';
|
||||||
import { firstValueFrom, of } from 'rxjs';
|
import { firstValueFrom, of } from 'rxjs';
|
||||||
import { catchError } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class EntityLogService extends GenericService<unknown> {
|
export class EntityLogService extends GenericService<unknown> {
|
||||||
protected readonly _defaultModelPath = '';
|
|
||||||
readonly #toaster = inject(Toaster);
|
readonly #toaster = inject(Toaster);
|
||||||
|
protected readonly _defaultModelPath = '';
|
||||||
|
|
||||||
async getEntityLog(dossierId: string, fileId: string) {
|
async getEntityLog(dossierId: string, fileId: string) {
|
||||||
const queryParams = [{ key: 'includeUnprocessed', value: true }];
|
const queryParams = [{ key: 'includeUnprocessed', value: true }];
|
||||||
const entityLog$ = this._getOne<IEntityLog>([dossierId, fileId], 'entityLog', queryParams);
|
const entityLog$ = this._getOne<IEntityLog>([dossierId, fileId], 'entityLog', queryParams);
|
||||||
const entityLog = await firstValueFrom(entityLog$.pipe(catchError(() => of({} as IEntityLog))));
|
const entityLog = await firstValueFrom(entityLog$.pipe(catchError(() => of({} as IEntityLog))));
|
||||||
entityLog.entityLogEntry = this.#filterInvalidEntries(entityLog.entityLogEntry);
|
entityLog.entityLogEntry = this.#filterInvalidEntries(entityLog.entityLogEntry, [EntryStates.REMOVED]);
|
||||||
entityLog.entityLogEntry.sort((a, b) => a.positions[0].pageNumber - b.positions[0].pageNumber);
|
entityLog.entityLogEntry.sort((a, b) => a.positions[0].pageNumber - b.positions[0].pageNumber);
|
||||||
return entityLog;
|
return entityLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
|
getDelta(dossierId: string, fileId: string, analysisNumber: number) {
|
||||||
|
const req$ = this._getOne<IEntityLog>([dossierId, fileId, analysisNumber.toString()], 'entityLog');
|
||||||
|
return req$.pipe(
|
||||||
|
map(entityLog => {
|
||||||
|
entityLog.entityLogEntry = this.#filterInvalidEntries(entityLog.entityLogEntry);
|
||||||
|
return entityLog;
|
||||||
|
}),
|
||||||
|
catchError(() => of({} as IEntityLog)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSectionGrid(dossierId: string, fileId: string) {
|
||||||
|
return this._getOne<ISectionGrid>([dossierId, fileId], 'sectionGrid');
|
||||||
|
}
|
||||||
|
|
||||||
|
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[], invalidStates: EntryState[] = []) {
|
||||||
return entityLogEntry.filter(entry => {
|
return entityLogEntry.filter(entry => {
|
||||||
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);
|
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);
|
||||||
const hasPositions = !!entry.positions?.length;
|
const hasPositions = !!entry.positions?.length;
|
||||||
const isRemoved = entry.state === EntryStates.REMOVED;
|
const hasInvalidState = invalidStates.includes(entry.state);
|
||||||
if (!hasPositions) {
|
if (!hasPositions) {
|
||||||
this.#toaster.devInfo(`Entry ${entry.id} was skipped because it has no position`);
|
this.#toaster.devInfo(`Entry ${entry.id} was skipped because it has no position`);
|
||||||
}
|
}
|
||||||
return hasPositions && !isRemoved;
|
return hasPositions && !hasInvalidState;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
73
apps/red-ui/src/app/services/stomp.service.ts
Normal file
73
apps/red-ui/src/app/services/stomp.service.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { inject } from '@angular/core';
|
||||||
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
|
import { KeycloakStatusService } from '@common-ui/tenants';
|
||||||
|
import { log } from '@common-ui/utils';
|
||||||
|
import { getConfig } from '@iqser/common-ui';
|
||||||
|
import { IMessage, IWatchParams, RxStomp } from '@stomp/rx-stomp';
|
||||||
|
import { StompHeaders } from '@stomp/stompjs';
|
||||||
|
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { firstValueFrom, Observable } from 'rxjs';
|
||||||
|
import { map, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export abstract class StompService extends RxStomp {
|
||||||
|
readonly #logger = inject(NGXLogger);
|
||||||
|
readonly #config = getConfig();
|
||||||
|
readonly #keycloakStatusService = inject(KeycloakStatusService);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.connectionState$.pipe(log('[WS] Connection state'), takeUntilDestroyed()).subscribe();
|
||||||
|
this.webSocketErrors$.pipe(log('[WS] Errors'), takeUntilDestroyed()).subscribe();
|
||||||
|
this.stompErrors$.pipe(takeUntilDestroyed()).subscribe(frame => {
|
||||||
|
console.error(frame);
|
||||||
|
console.error('Broker reported error: ' + frame.headers['message']);
|
||||||
|
console.error('Additional details: ' + frame.body);
|
||||||
|
});
|
||||||
|
this.#keycloakStatusService.token$.pipe(takeUntilDestroyed()).subscribe(token => {
|
||||||
|
this.#logger.info('[WS] Update connectHeaders');
|
||||||
|
this.configure({
|
||||||
|
connectHeaders: { Authorization: 'Bearer ' + token },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get topicPrefix() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
send(value: unknown) {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
override watch(opts: IWatchParams): Observable<IMessage>;
|
||||||
|
override watch(destination: string, headers?: StompHeaders): Observable<IMessage>;
|
||||||
|
override watch(opts: string | IWatchParams, headers?: StompHeaders): Observable<IMessage> {
|
||||||
|
if (typeof opts === 'string') {
|
||||||
|
return super.watch(this.topicPrefix + opts, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.watch(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
listen<T>(topic: string): Observable<T> {
|
||||||
|
return this.watch(topic).pipe(
|
||||||
|
tap(msg => this.#logger.info('[WS] Response on topic ' + topic, msg.body)),
|
||||||
|
map(msg => JSON.parse(msg.body)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(url: string) {
|
||||||
|
this.configure({
|
||||||
|
debug: (msg: string) => this.#logger.debug('[WS] ' + msg),
|
||||||
|
brokerURL: this.#config.API_URL + url,
|
||||||
|
reconnectDelay: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectAsync(url: string) {
|
||||||
|
return await firstValueFrom(this.#keycloakStatusService.token$.pipe(map(() => this.connect(url))));
|
||||||
|
}
|
||||||
|
}
|
||||||
14
apps/red-ui/src/app/services/web-socket.service.ts
Normal file
14
apps/red-ui/src/app/services/web-socket.service.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { inject, Injectable } from '@angular/core';
|
||||||
|
import { TenantsService } from '@common-ui/tenants';
|
||||||
|
import { StompService } from '@services/stomp.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class WebSocketService extends StompService {
|
||||||
|
readonly #tenantService = inject(TenantsService);
|
||||||
|
|
||||||
|
override get topicPrefix() {
|
||||||
|
return '/topic/' + this.#tenantService.activeTenantId + '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -44,6 +44,10 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
|||||||
error: _('annotation-actions.message.manual-redaction.recategorize-annotation.error'),
|
error: _('annotation-actions.message.manual-redaction.recategorize-annotation.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.recategorize-annotation.success'),
|
success: _('annotation-actions.message.manual-redaction.recategorize-annotation.success'),
|
||||||
},
|
},
|
||||||
|
'revert-changes': {
|
||||||
|
success: _('annotation-actions.message.manual-redaction.revert-changes.success'),
|
||||||
|
error: _('annotation-actions.message.manual-redaction.revert-changes.error'),
|
||||||
|
},
|
||||||
undo: {
|
undo: {
|
||||||
error: _('annotation-actions.message.manual-redaction.undo.error'),
|
error: _('annotation-actions.message.manual-redaction.undo.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.undo.success'),
|
success: _('annotation-actions.message.manual-redaction.undo.success'),
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { inject, Injectable } from '@angular/core';
|
|||||||
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||||
import { IqserRoleGuard } from '@iqser/common-ui/lib/users';
|
import { IqserRoleGuard } from '@iqser/common-ui/lib/users';
|
||||||
import { UserService } from '@users/user.service';
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -10,10 +9,9 @@ import { NGXLogger } from 'ngx-logger';
|
|||||||
})
|
})
|
||||||
export class RedRoleGuard extends IqserRoleGuard {
|
export class RedRoleGuard extends IqserRoleGuard {
|
||||||
protected readonly _permissionsService = inject(IqserPermissionsService);
|
protected readonly _permissionsService = inject(IqserPermissionsService);
|
||||||
protected readonly _userService = inject(UserService);
|
|
||||||
protected readonly _logger = inject(NGXLogger);
|
protected readonly _logger = inject(NGXLogger);
|
||||||
|
|
||||||
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
override async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
|
||||||
const currentUser = this._userService.currentUser;
|
const currentUser = this._userService.currentUser;
|
||||||
|
|
||||||
if (!currentUser?.hasAnyRole) {
|
if (!currentUser?.hasAnyRole) {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { User } from '@red/domain';
|
|
||||||
import { QueryParam } from '@iqser/common-ui';
|
import { QueryParam } from '@iqser/common-ui';
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { of } from 'rxjs';
|
|
||||||
import { IIqserUser, IqserUserService } from '@iqser/common-ui/lib/users';
|
import { IIqserUser, IqserUserService } from '@iqser/common-ui/lib/users';
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { User } from '@red/domain';
|
||||||
|
import { Roles } from '@users/roles';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -13,7 +13,7 @@ export class UserService extends IqserUserService<IIqserUser, User> {
|
|||||||
protected readonly _defaultModelPath = 'user';
|
protected readonly _defaultModelPath = 'user';
|
||||||
protected readonly _entityClass = User;
|
protected readonly _entityClass = User;
|
||||||
|
|
||||||
async loadCurrentUser(): Promise<User | undefined> {
|
override async loadCurrentUser(): Promise<User | undefined> {
|
||||||
const currentUser = await super.loadCurrentUser();
|
const currentUser = await super.loadCurrentUser();
|
||||||
|
|
||||||
this._permissionsService.add({
|
this._permissionsService.add({
|
||||||
@ -23,12 +23,12 @@ export class UserService extends IqserUserService<IIqserUser, User> {
|
|||||||
return currentUser;
|
return currentUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAll() {
|
override loadAll() {
|
||||||
const canReadUsers = this._permissionsService.has(Roles.users.read);
|
const canReadUsers = this._permissionsService.has(Roles.users.read);
|
||||||
return canReadUsers ? super.loadAll() : of([]);
|
return canReadUsers ? super.loadAll() : of([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll() {
|
override getAll() {
|
||||||
const canReadAllUsers = this._permissionsService.has(Roles.users.readAll);
|
const canReadAllUsers = this._permissionsService.has(Roles.users.readAll);
|
||||||
const url = canReadAllUsers ? this._defaultModelPath : `${this._defaultModelPath}/red`;
|
const url = canReadAllUsers ? this._defaultModelPath : `${this._defaultModelPath}/red`;
|
||||||
return super.getAll(url);
|
return super.getAll(url);
|
||||||
@ -39,8 +39,8 @@ export class UserService extends IqserUserService<IIqserUser, User> {
|
|||||||
return this._post(null, `${this._defaultModelPath}/profile/activate/${user.userId}`, queryParams);
|
return this._post(null, `${this._defaultModelPath}/profile/activate/${user.userId}`, queryParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly _rolesFilter = (role: string) => role.startsWith('RED_');
|
protected override readonly _rolesFilter = (role: string) => role.startsWith('RED_');
|
||||||
protected readonly _permissionsFilter = (role: string) => role.startsWith('red-') || role.startsWith('fforesight-');
|
protected override readonly _permissionsFilter = (role: string) => role.startsWith('red-') || role.startsWith('fforesight-');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCurrentUser() {
|
export function getCurrentUser() {
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export function mainGuard(): AsyncGuard {
|
|||||||
userPreferenceService.reload(),
|
userPreferenceService.reload(),
|
||||||
firstValueFrom(updatedDisplayName$),
|
firstValueFrom(updatedDisplayName$),
|
||||||
]);
|
]);
|
||||||
|
await tenantsService.storeTenant();
|
||||||
await languageService.setInitialLanguage();
|
await languageService.setInitialLanguage();
|
||||||
const lastDossierTemplate = userPreferenceService.getLastDossierTemplate();
|
const lastDossierTemplate = userPreferenceService.getLastDossierTemplate();
|
||||||
|
|
||||||
@ -60,8 +60,6 @@ export function mainGuard(): AsyncGuard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadingService.stop();
|
loadingService.stop();
|
||||||
|
|
||||||
await tenantsService.storeTenant();
|
|
||||||
configService.updateIsDocumine(tenantsService.activeTenant);
|
configService.updateIsDocumine(tenantsService.activeTenant);
|
||||||
|
|
||||||
logger.info('[ROUTES] Main guard finished!');
|
logger.info('[ROUTES] Main guard finished!');
|
||||||
|
|||||||
@ -577,6 +577,11 @@
|
|||||||
"documentKey": "dossier_stop_analysis",
|
"documentKey": "dossier_stop_analysis",
|
||||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"elementKey": "dossier_analyze_file",
|
||||||
|
"documentKey": "analyze_file",
|
||||||
|
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"elementKey": "dossier_automatic_text_recognition",
|
"elementKey": "dossier_automatic_text_recognition",
|
||||||
"documentKey": "dossier_automatic_text_recognition",
|
"documentKey": "dossier_automatic_text_recognition",
|
||||||
@ -626,7 +631,6 @@
|
|||||||
"elementKey": "workload_page_list",
|
"elementKey": "workload_page_list",
|
||||||
"documentKey": "workload_page_list"
|
"documentKey": "workload_page_list"
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"elementKey": "editor_delete_file",
|
"elementKey": "editor_delete_file",
|
||||||
"documentKey": "editor_delete_file"
|
"documentKey": "editor_delete_file"
|
||||||
@ -792,10 +796,6 @@
|
|||||||
"elementKey": "components_table",
|
"elementKey": "components_table",
|
||||||
"documentKey": "components_table"
|
"documentKey": "components_table"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"elementKey": "components_table",
|
|
||||||
"documentKey": "components_table"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"elementKey": "remove_annotation_DIALOG",
|
"elementKey": "remove_annotation_DIALOG",
|
||||||
"documentKey": "remove_annotation"
|
"documentKey": "remove_annotation"
|
||||||
|
|||||||
@ -341,6 +341,10 @@
|
|||||||
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
|
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
|
||||||
"success": "Hinweis wurde entfernt"
|
"success": "Hinweis wurde entfernt"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"error": "Manuelle Änderungen konnten nicht rückgängig gemacht werden: {error}",
|
||||||
|
"success": "Manuelle Änderungen wurden erfolgreich rückgängig gemacht."
|
||||||
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
"success": "Rücksetzung erfolgreich"
|
"success": "Rücksetzung erfolgreich"
|
||||||
@ -362,6 +366,9 @@
|
|||||||
"resize-cancel": {
|
"resize-cancel": {
|
||||||
"label": "Größenänderung abbrechen"
|
"label": "Größenänderung abbrechen"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"label": "Änderungen rückgängig machen"
|
||||||
|
},
|
||||||
"see-references": {
|
"see-references": {
|
||||||
"label": "Referenzen anzeigen"
|
"label": "Referenzen anzeigen"
|
||||||
},
|
},
|
||||||
@ -553,6 +560,9 @@
|
|||||||
"undo": "Zurücksetzen"
|
"undo": "Zurücksetzen"
|
||||||
},
|
},
|
||||||
"components": "Komponenten",
|
"components": "Komponenten",
|
||||||
|
"filter": {
|
||||||
|
"search-placeholder": "Suche nach Komponente..."
|
||||||
|
},
|
||||||
"table-header": {
|
"table-header": {
|
||||||
"component": "Komponente",
|
"component": "Komponente",
|
||||||
"value": "Wert"
|
"value": "Wert"
|
||||||
@ -702,6 +712,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content": "Grund",
|
"content": "Grund",
|
||||||
|
"copilot": {
|
||||||
|
"label": "Copilot"
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"empty-template": {
|
"empty-template": {
|
||||||
"description": "Diese Vorlage enthält keine Dossiers. Erstellen Sie ein Dossier, um nach diesen Regeln zu schwärzen.",
|
"description": "Diese Vorlage enthält keine Dossiers. Erstellen Sie ein Dossier, um nach diesen Regeln zu schwärzen.",
|
||||||
@ -2365,6 +2378,16 @@
|
|||||||
"header": "Größe von {type} ändern"
|
"header": "Größe von {type} ändern"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"revert-manual-changes-dialog": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"save": "Zu den Originalen zurückkehren"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"title": "Manuelle Änderungen"
|
||||||
|
},
|
||||||
|
"title": "Lokale manuelle Änderungen rückgängig machen"
|
||||||
|
},
|
||||||
"revert-value-dialog": {
|
"revert-value-dialog": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
|
|||||||
@ -338,6 +338,10 @@
|
|||||||
"error": "Failed to remove redaction: {error}",
|
"error": "Failed to remove redaction: {error}",
|
||||||
"success": "Redaction removed"
|
"success": "Redaction removed"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"error": "Failed to revert manual changes: {error}",
|
||||||
|
"success": "Reverted manual changes successfully."
|
||||||
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -359,6 +363,9 @@
|
|||||||
"resize": {
|
"resize": {
|
||||||
"label": "Resize"
|
"label": "Resize"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"label": "Revert changes"
|
||||||
|
},
|
||||||
"see-references": {
|
"see-references": {
|
||||||
"label": "See references"
|
"label": "See references"
|
||||||
},
|
},
|
||||||
@ -553,6 +560,9 @@
|
|||||||
"undo": "Undo"
|
"undo": "Undo"
|
||||||
},
|
},
|
||||||
"components": "Components",
|
"components": "Components",
|
||||||
|
"filter": {
|
||||||
|
"search-placeholder": "Search by component..."
|
||||||
|
},
|
||||||
"table-header": {
|
"table-header": {
|
||||||
"component": "Component",
|
"component": "Component",
|
||||||
"value": "Value"
|
"value": "Value"
|
||||||
@ -632,7 +642,7 @@
|
|||||||
"confirm-delete-users": {
|
"confirm-delete-users": {
|
||||||
"cancel": "Keep {usersCount, plural, one{user} other{users}}",
|
"cancel": "Keep {usersCount, plural, one{user} other{users}}",
|
||||||
"delete": "Delete {usersCount, plural, one{user} other{users}}",
|
"delete": "Delete {usersCount, plural, one{user} other{users}}",
|
||||||
"impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted",
|
"impacted-documents": "{documentsCount} {documentsCount, plural, one{document} other{documents}} will be impacted",
|
||||||
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
|
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
|
||||||
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
|
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
|
||||||
"toast-error": "Please confirm that you understand the consequences of this action.",
|
"toast-error": "Please confirm that you understand the consequences of this action.",
|
||||||
@ -702,6 +712,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content": "Reason",
|
"content": "Reason",
|
||||||
|
"copilot": {
|
||||||
|
"label": "Copilot"
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"empty-template": {
|
"empty-template": {
|
||||||
"description": "This template does not contain any dossiers. Create a dossier that applies this ruleset.",
|
"description": "This template does not contain any dossiers. Create a dossier that applies this ruleset.",
|
||||||
@ -2365,6 +2378,16 @@
|
|||||||
"header": "Resize {type}"
|
"header": "Resize {type}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"revert-manual-changes-dialog": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"save": "Revert to originals"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"title": "Manual changes"
|
||||||
|
},
|
||||||
|
"title": "Revert local manual changes"
|
||||||
|
},
|
||||||
"revert-value-dialog": {
|
"revert-value-dialog": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
|||||||
@ -225,7 +225,7 @@
|
|||||||
"actions": {
|
"actions": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"save-and-remember": "Save and remember my choice"
|
"save-and-remember": "Speichern und Auswahl merken"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"comment": "Kommentar",
|
"comment": "Kommentar",
|
||||||
@ -243,7 +243,7 @@
|
|||||||
},
|
},
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"type-placeholder": "Typ auswählen...",
|
"type-placeholder": "Typ auswählen...",
|
||||||
"value": "Wert\n"
|
"value": "Wert"
|
||||||
},
|
},
|
||||||
"title": "Hinweis hinzufügen"
|
"title": "Hinweis hinzufügen"
|
||||||
}
|
}
|
||||||
@ -275,6 +275,9 @@
|
|||||||
"watermarks": "Wasserzeichen"
|
"watermarks": "Wasserzeichen"
|
||||||
},
|
},
|
||||||
"analysis-disabled": "Analyse deaktiviert",
|
"analysis-disabled": "Analyse deaktiviert",
|
||||||
|
"annotation": {
|
||||||
|
"pending": "(Analyse steht aus)"
|
||||||
|
},
|
||||||
"annotation-actions": {
|
"annotation-actions": {
|
||||||
"accept-recommendation": {
|
"accept-recommendation": {
|
||||||
"label": "Empfehlung annehmen"
|
"label": "Empfehlung annehmen"
|
||||||
@ -330,13 +333,17 @@
|
|||||||
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
|
"error": "Rekategorisierung des Bilds fehlgeschlagen: {error}",
|
||||||
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
||||||
},
|
},
|
||||||
|
"remove": {
|
||||||
|
"error": "Entfernen der Annotation fehlgeschlagen: {error}",
|
||||||
|
"success": "Annotation wurde entfernt"
|
||||||
|
},
|
||||||
"remove-hint": {
|
"remove-hint": {
|
||||||
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
|
"error": "Entfernen des Hinweises fehlgeschlagen: {error}",
|
||||||
"success": "Hinweis wurde entfernt"
|
"success": "Hinweis wurde entfernt"
|
||||||
},
|
},
|
||||||
"remove": {
|
"revert-changes": {
|
||||||
"error": "Entfernen der Annotation fehlgeschlagen: {error}",
|
"error": "Manuelle Änderungen konnten nicht rückgängig gemacht werden: {error}",
|
||||||
"success": "Annotation wurde entfernt"
|
"success": "Manuelle Änderungen wurden erfolgreich rückgängig gemacht."
|
||||||
},
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
@ -350,14 +357,17 @@
|
|||||||
"remove-highlights": {
|
"remove-highlights": {
|
||||||
"label": "Ausgewählte Markierungen entfernen"
|
"label": "Ausgewählte Markierungen entfernen"
|
||||||
},
|
},
|
||||||
|
"resize": {
|
||||||
|
"label": "Größe ändern"
|
||||||
|
},
|
||||||
"resize-accept": {
|
"resize-accept": {
|
||||||
"label": "Neue Größe speichern"
|
"label": "Neue Größe speichern"
|
||||||
},
|
},
|
||||||
"resize-cancel": {
|
"resize-cancel": {
|
||||||
"label": "Größenänderung abbrechen"
|
"label": "Größenänderung abbrechen"
|
||||||
},
|
},
|
||||||
"resize": {
|
"revert-changes": {
|
||||||
"label": "Größe ändern"
|
"label": "Änderungen rückgängig machen"
|
||||||
},
|
},
|
||||||
"see-references": {
|
"see-references": {
|
||||||
"label": "Referenzen anzeigen"
|
"label": "Referenzen anzeigen"
|
||||||
@ -375,7 +385,7 @@
|
|||||||
"removed-manual": "Schwärzung/Hinweis entfernt",
|
"removed-manual": "Schwärzung/Hinweis entfernt",
|
||||||
"resized": "Schwärzungsbereich wurde geändert"
|
"resized": "Schwärzungsbereich wurde geändert"
|
||||||
},
|
},
|
||||||
"annotation-content": "{hasRule, select, true {Rule {matchedRule} trifft zu:{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n Abschnitt{sectionSymbol} \"{section}\"} other {}}",
|
"annotation-content": "{hasRule, select, true {Rule {matchedRule} trifft zu:{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n Abschnitt{sectionSymbol} \\\"{section}\\\"} other {}}",
|
||||||
"annotation-engines": {
|
"annotation-engines": {
|
||||||
"dictionary": "{isHint, select, true{Hinweis} other{Schwärzung}} basiert auf Wörterbuch",
|
"dictionary": "{isHint, select, true{Hinweis} other{Schwärzung}} basiert auf Wörterbuch",
|
||||||
"dossier-dictionary": "Annotation basiert auf Dossier-Wörterbuch",
|
"dossier-dictionary": "Annotation basiert auf Dossier-Wörterbuch",
|
||||||
@ -393,9 +403,6 @@
|
|||||||
"skipped": "Übersprungen",
|
"skipped": "Übersprungen",
|
||||||
"text-highlight": "Markierung"
|
"text-highlight": "Markierung"
|
||||||
},
|
},
|
||||||
"annotation": {
|
|
||||||
"pending": "(Analyse steht aus)"
|
|
||||||
},
|
|
||||||
"annotations": "Annotationen",
|
"annotations": "Annotationen",
|
||||||
"archived-dossiers-listing": {
|
"archived-dossiers-listing": {
|
||||||
"no-data": {
|
"no-data": {
|
||||||
@ -525,11 +532,11 @@
|
|||||||
"add-title": "Neue Definition hinzufügen",
|
"add-title": "Neue Definition hinzufügen",
|
||||||
"columns": {
|
"columns": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"position": "Pos."
|
"position": "pos."
|
||||||
},
|
},
|
||||||
"edit-title": "Definition von {displayName} bearbeiten",
|
"edit-title": "Definition von {displayName} bearbeiten",
|
||||||
"form": {
|
"form": {
|
||||||
"autogenerated-label": "Wird ausgehend vom ersten Anzeigenamen automatisch generiert.",
|
"autogenerated-label": "Wurde ausgehend vom initialen Anzeigenamen automatisch generiert",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"description-placeholder": "Beschreibung",
|
"description-placeholder": "Beschreibung",
|
||||||
"display-name": "Anzeigename",
|
"display-name": "Anzeigename",
|
||||||
@ -553,6 +560,9 @@
|
|||||||
"undo": "Zurücksetzen"
|
"undo": "Zurücksetzen"
|
||||||
},
|
},
|
||||||
"components": "Komponenten",
|
"components": "Komponenten",
|
||||||
|
"filter": {
|
||||||
|
"search-placeholder": "Suche nach Komponente..."
|
||||||
|
},
|
||||||
"table-header": {
|
"table-header": {
|
||||||
"component": "Komponente",
|
"component": "Komponente",
|
||||||
"value": "Wert"
|
"value": "Wert"
|
||||||
@ -571,7 +581,7 @@
|
|||||||
},
|
},
|
||||||
"search": "Nach Name suchen...",
|
"search": "Nach Name suchen...",
|
||||||
"table-col-names": {
|
"table-col-names": {
|
||||||
"column-labels": "Spaltenbeschriftungen",
|
"column-labels": "Column labels",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"number-of-lines": "Zeilenzahl",
|
"number-of-lines": "Zeilenzahl",
|
||||||
"version": "Version"
|
"version": "Version"
|
||||||
@ -614,7 +624,7 @@
|
|||||||
"impacted-report": "{reportsCount} Berichte nutzen den Platzhalter dieses Attributs. Bitte aktualisieren Sie diese.",
|
"impacted-report": "{reportsCount} Berichte nutzen den Platzhalter dieses Attributs. Bitte aktualisieren Sie diese.",
|
||||||
"title": "{count, plural, one{Attribut} other{Attribute}} löschen",
|
"title": "{count, plural, one{Attribut} other{Attribute}} löschen",
|
||||||
"toast-error": "Bitte bestätigen Sie, dass die Folgen dieser Aktion verstehen.",
|
"toast-error": "Bitte bestätigen Sie, dass die Folgen dieser Aktion verstehen.",
|
||||||
"warning": "Warnung: Wiederherstellung des Attributs nicht möglich."
|
"warning": "Warnung: Aktion kann nicht rückgängig gemacht werden."
|
||||||
},
|
},
|
||||||
"confirm-delete-dossier-state": {
|
"confirm-delete-dossier-state": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
@ -636,18 +646,18 @@
|
|||||||
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{Dossier} other{Dossiers}} sind betroffen",
|
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{Dossier} other{Dossiers}} sind betroffen",
|
||||||
"title": "{usersCount, plural, one{Benutzer} other{Benutzer}} aus Workspace entfernen",
|
"title": "{usersCount, plural, one{Benutzer} other{Benutzer}} aus Workspace entfernen",
|
||||||
"toast-error": "Bitte bestätigen Sie, dass Sie die Folgen dieser Aktion verstehen.",
|
"toast-error": "Bitte bestätigen Sie, dass Sie die Folgen dieser Aktion verstehen.",
|
||||||
"warning": "Warnung: Wiederherstellung des Benutzers nicht möglich."
|
"warning": "Warnung: Aktion kann nicht rückgängig gemacht werden."
|
||||||
},
|
},
|
||||||
"confirmation-dialog": {
|
"confirmation-dialog": {
|
||||||
"approve-file": {
|
"approve-file": {
|
||||||
"confirmationText": "Trotzdem genehmigen",
|
"confirmationText": "Dennoch freigeben",
|
||||||
"denyText": "Nein, abbrechen",
|
"denyText": "Nein, abbrechen",
|
||||||
"question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
|
"question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.<br><br>Möchten Sie es trotzdem freigeben?",
|
||||||
"title": "Warnung!",
|
"title": "Warnung!",
|
||||||
"warning-reason": {
|
"warning-reason": {
|
||||||
"legal-basis-missing": "Rechtsgrundlage fehlt",
|
"legal-basis-missing": "Legal basis missing",
|
||||||
"pending-changes": "Änderungen stehen aus",
|
"pending-changes": "Ausstehende Änderungen",
|
||||||
"unmapped-justification": "Nicht gemappte Begründung"
|
"unmapped-justification": "Unmapped justification"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"assign-file-to-me": {
|
"assign-file-to-me": {
|
||||||
@ -702,6 +712,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content": "Grund",
|
"content": "Grund",
|
||||||
|
"copilot": {
|
||||||
|
"label": "Copilot"
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"empty-template": {
|
"empty-template": {
|
||||||
"description": "Diese Vorlage enthält keine Dossiers. Erstellen Sie ein Dossier, um nach diesen Regeln zu schwärzen.",
|
"description": "Diese Vorlage enthält keine Dossiers. Erstellen Sie ein Dossier, um nach diesen Regeln zu schwärzen.",
|
||||||
@ -928,7 +941,7 @@
|
|||||||
"action": "Ganzes Dossier analysieren"
|
"action": "Ganzes Dossier analysieren"
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"timeoutError": "Regeln für Dossier-Vorlagen gesperrt!"
|
"timeoutError": "Regeln der Dossier-Vorlage gesperrt!"
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"analyzed-pages": "{count, plural, one{Seite} other{Seiten}}",
|
"analyzed-pages": "{count, plural, one{Seite} other{Seiten}}",
|
||||||
@ -945,7 +958,7 @@
|
|||||||
"table-header": {
|
"table-header": {
|
||||||
"title": "{length} {length, plural, one{aktives Dossier} other{aktive Dossiers}}"
|
"title": "{length} {length, plural, one{aktives Dossier} other{aktive Dossiers}}"
|
||||||
},
|
},
|
||||||
"template-inactive": "Aktuell ausgewählte Dossier-Vorlage inaktiv!"
|
"template-inactive": "Die aktuell ausgewählte Dossier-Vorlage ist inaktiv."
|
||||||
},
|
},
|
||||||
"dossier-overview": {
|
"dossier-overview": {
|
||||||
"approve": "Genehmigen",
|
"approve": "Genehmigen",
|
||||||
@ -1023,13 +1036,13 @@
|
|||||||
"recent": "Neu ({hours} h)",
|
"recent": "Neu ({hours} h)",
|
||||||
"unassigned": "Niemandem zugewiesen"
|
"unassigned": "Niemandem zugewiesen"
|
||||||
},
|
},
|
||||||
|
"reanalyse": {
|
||||||
|
"action": "Datei analysieren"
|
||||||
|
},
|
||||||
"reanalyse-dossier": {
|
"reanalyse-dossier": {
|
||||||
"error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.",
|
"error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.",
|
||||||
"success": "Dateien für Reanalyse vorgesehen."
|
"success": "Dateien für Reanalyse vorgesehen."
|
||||||
},
|
},
|
||||||
"reanalyse": {
|
|
||||||
"action": "Datei analysieren"
|
|
||||||
},
|
|
||||||
"report-download": "Bericht herunterladen",
|
"report-download": "Bericht herunterladen",
|
||||||
"start-auto-analysis": "Auto-Analyse aktivieren",
|
"start-auto-analysis": "Auto-Analyse aktivieren",
|
||||||
"stop-auto-analysis": "Auto-Analyse anhalten",
|
"stop-auto-analysis": "Auto-Analyse anhalten",
|
||||||
@ -1105,6 +1118,14 @@
|
|||||||
"total-documents": "Dokumente",
|
"total-documents": "Dokumente",
|
||||||
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
|
"total-people": "<strong>{count}</strong> {count, plural, one{Benutzer} other {Benutzer}}"
|
||||||
},
|
},
|
||||||
|
"dossier-templates": {
|
||||||
|
"label": "Dossier-Vorlagen",
|
||||||
|
"status": {
|
||||||
|
"active": "Aktiv",
|
||||||
|
"inactive": "Inaktiv",
|
||||||
|
"incomplete": "Unvollständig"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dossier-templates-listing": {
|
"dossier-templates-listing": {
|
||||||
"action": {
|
"action": {
|
||||||
"clone": "Vorlage klonen",
|
"clone": "Vorlage klonen",
|
||||||
@ -1139,14 +1160,6 @@
|
|||||||
"title": "{length} {length, plural, one{archiviertes Dossier} other{archivierte Dossiers}}"
|
"title": "{length} {length, plural, one{archiviertes Dossier} other{archivierte Dossiers}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dossier-templates": {
|
|
||||||
"label": "Dossier-Vorlagen",
|
|
||||||
"status": {
|
|
||||||
"active": "Aktiv",
|
|
||||||
"inactive": "Inaktiv",
|
|
||||||
"incomplete": "Unvollständig"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dossier-watermark-selector": {
|
"dossier-watermark-selector": {
|
||||||
"heading": "Wasserzeichen auf Dokumenten",
|
"heading": "Wasserzeichen auf Dokumenten",
|
||||||
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
|
"no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:<br>Bitten Sie Ihren Admin, eines zu konfigurieren.",
|
||||||
@ -1314,7 +1327,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"in-document": {
|
"in-document": {
|
||||||
"description": "",
|
"description": "",
|
||||||
"label": "In Dokument ändern"
|
"label": "Im Dokument bearbeiten"
|
||||||
},
|
},
|
||||||
"only-here": {
|
"only-here": {
|
||||||
"description": "",
|
"description": "",
|
||||||
@ -1357,6 +1370,15 @@
|
|||||||
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
|
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"entity": {
|
||||||
|
"info": {
|
||||||
|
"actions": {
|
||||||
|
"revert": "Zurücksetzen",
|
||||||
|
"save": "Änderungen speichern"
|
||||||
|
},
|
||||||
|
"heading": "Entität bearbeiten"
|
||||||
|
}
|
||||||
|
},
|
||||||
"entity-rules-screen": {
|
"entity-rules-screen": {
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
|
"generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen."
|
||||||
@ -1370,28 +1392,19 @@
|
|||||||
"title": "Entitätsregeln-Editor",
|
"title": "Entitätsregeln-Editor",
|
||||||
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
|
"warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden"
|
||||||
},
|
},
|
||||||
"entity": {
|
|
||||||
"info": {
|
|
||||||
"actions": {
|
|
||||||
"revert": "Zurücksetzen",
|
|
||||||
"save": "Änderungen speichern"
|
|
||||||
},
|
|
||||||
"heading": "Entität bearbeiten"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"error": {
|
"error": {
|
||||||
"deleted-entity": {
|
"deleted-entity": {
|
||||||
"dossier": {
|
"dossier": {
|
||||||
"action": "Zurück zur Übersicht",
|
"action": "Zurück zur Übersicht",
|
||||||
"label": "Dieses Dossier wurde gelöscht."
|
"label": "Dieses Dossier wurde gelöscht."
|
||||||
},
|
},
|
||||||
"file-dossier": {
|
|
||||||
"action": "Zurück zur Übersicht",
|
|
||||||
"label": "Das Dossier dieser Datei wurde gelöscht."
|
|
||||||
},
|
|
||||||
"file": {
|
"file": {
|
||||||
"action": "Zurück zum Dossier",
|
"action": "Zurück zum Dossier",
|
||||||
"label": "Diese Datei wurde gelöscht."
|
"label": "Diese Datei wurde gelöscht."
|
||||||
|
},
|
||||||
|
"file-dossier": {
|
||||||
|
"action": "Zurück zur Übersicht",
|
||||||
|
"label": "Das Dossier dieser Datei wurde gelöscht."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"file-preview": {
|
"file-preview": {
|
||||||
@ -1409,6 +1422,12 @@
|
|||||||
},
|
},
|
||||||
"exact-date": "{day} {month} {year} um {hour}:{minute} Uhr",
|
"exact-date": "{day} {month} {year} um {hour}:{minute} Uhr",
|
||||||
"file": "Datei",
|
"file": "Datei",
|
||||||
|
"file-attribute": {
|
||||||
|
"update": {
|
||||||
|
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
|
||||||
|
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
|
||||||
|
}
|
||||||
|
},
|
||||||
"file-attribute-encoding-types": {
|
"file-attribute-encoding-types": {
|
||||||
"ascii": "ASCII",
|
"ascii": "ASCII",
|
||||||
"iso": "ISO-8859-1",
|
"iso": "ISO-8859-1",
|
||||||
@ -1419,12 +1438,6 @@
|
|||||||
"number": "Nummer",
|
"number": "Nummer",
|
||||||
"text": "Freier Text"
|
"text": "Freier Text"
|
||||||
},
|
},
|
||||||
"file-attribute": {
|
|
||||||
"update": {
|
|
||||||
"error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.",
|
|
||||||
"success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file-attributes-configurations": {
|
"file-attributes-configurations": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"form": {
|
"form": {
|
||||||
@ -1639,18 +1652,9 @@
|
|||||||
"file-upload": {
|
"file-upload": {
|
||||||
"type": {
|
"type": {
|
||||||
"csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert.",
|
"csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert.",
|
||||||
"zip": ""
|
"zip": "Die Zip-Datei wurde erfolgreich hochgeladen!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filter-menu": {
|
|
||||||
"filter-options": "Filteroptionen",
|
|
||||||
"filter-types": "Filter",
|
|
||||||
"label": "Filter",
|
|
||||||
"pages-without-annotations": "Nur Seiten ohne Annotationen",
|
|
||||||
"redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen",
|
|
||||||
"unseen-pages": "Nur Anmerkungen auf ungesehenen Seiten",
|
|
||||||
"with-comments": "Nur Anmerkungen mit Kommentaren"
|
|
||||||
},
|
|
||||||
"filter": {
|
"filter": {
|
||||||
"analysis": "Analyse erforderlich",
|
"analysis": "Analyse erforderlich",
|
||||||
"comment": "Kommentare",
|
"comment": "Kommentare",
|
||||||
@ -1660,6 +1664,15 @@
|
|||||||
"redaction": "Annotationen",
|
"redaction": "Annotationen",
|
||||||
"updated": "Aktualisiert"
|
"updated": "Aktualisiert"
|
||||||
},
|
},
|
||||||
|
"filter-menu": {
|
||||||
|
"filter-options": "Filteroptionen",
|
||||||
|
"filter-types": "Filter",
|
||||||
|
"label": "Filter",
|
||||||
|
"pages-without-annotations": "Nur Seiten ohne Annotationen",
|
||||||
|
"redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen",
|
||||||
|
"unseen-pages": "Nur Anmerkungen auf ungesehenen Seiten",
|
||||||
|
"with-comments": "Nur Anmerkungen mit Kommentaren"
|
||||||
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"assigned-people": "Bearbeiter",
|
"assigned-people": "Bearbeiter",
|
||||||
"documents-status": "Dokumentenstatus",
|
"documents-status": "Dokumentenstatus",
|
||||||
@ -1728,11 +1741,11 @@
|
|||||||
"title": "SMTP-Konto konfigurieren"
|
"title": "SMTP-Konto konfigurieren"
|
||||||
},
|
},
|
||||||
"generic-errors": {
|
"generic-errors": {
|
||||||
"400": "",
|
"400": "Die gesendete Anfrage ist ungültig.",
|
||||||
"403": "",
|
"403": "Der Zugriff auf die angeforderte Ressource ist nicht zulässig.",
|
||||||
"404": "",
|
"404": "Die angeforderte Ressource konnte nicht gefunden werden.",
|
||||||
"409": "",
|
"409": "Die Anfrage ist nicht mit dem aktuellen Status kompatibel.",
|
||||||
"500": ""
|
"500": "Der Server hat einen unerwarteten Fehler festgestellt und konnte die Anfrage nicht bearbeiten."
|
||||||
},
|
},
|
||||||
"help-button": {
|
"help-button": {
|
||||||
"disable": "Hilfemodus deaktivieren",
|
"disable": "Hilfemodus deaktivieren",
|
||||||
@ -1904,15 +1917,15 @@
|
|||||||
"legalBasis": "Rechtsgrundlage",
|
"legalBasis": "Rechtsgrundlage",
|
||||||
"options": {
|
"options": {
|
||||||
"multiple-pages": {
|
"multiple-pages": {
|
||||||
"description": "Annotation auf folgenden Seiten bearbeiten",
|
"description": "Fügen Sie die Annotation auf mehreren Seiten hinzu.",
|
||||||
"extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.",
|
"extraOptionDescription": "Minus (-) für Bereich und Komma (,) für Aufzählung.",
|
||||||
"extraOptionLabel": "Seitenbereich",
|
"extraOptionLabel": "Seiten",
|
||||||
"extraOptionPlaceholder": "z. B. 1-20,22,32",
|
"extraOptionPlaceholder": "z. B. 1-20,22,32",
|
||||||
"label": "Auf mehreren Seiten anwenden"
|
"label": "Auf mehreren Seiten anwenden"
|
||||||
},
|
},
|
||||||
"only-this-page": {
|
"only-this-page": {
|
||||||
"description": "Annotation nur an dieser Position im Dokument bearbeiten",
|
"description": "Fügen Sie die Annotation nur an dieser Stelle im Dokument hinzu.",
|
||||||
"label": "Auf dieser Seite anwenden"
|
"label": "Nur auf dieser Seite anwenden"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"reason": "Grund",
|
"reason": "Grund",
|
||||||
@ -1939,15 +1952,22 @@
|
|||||||
"document-approved": "<b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> wurde genehmigt.",
|
"document-approved": "<b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> wurde genehmigt.",
|
||||||
"dossier-deleted": "Dossier wurde gelöscht: <b>{dossierName}</b>",
|
"dossier-deleted": "Dossier wurde gelöscht: <b>{dossierName}</b>",
|
||||||
"dossier-owner-deleted": "Der Besitzer des Dossiers wurde gelöscht: <b>{dossierName}</b>",
|
"dossier-owner-deleted": "Der Besitzer des Dossiers wurde gelöscht: <b>{dossierName}</b>",
|
||||||
"dossier-owner-removed": "Der Dossier-Owner von <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> wurde entfernt!",
|
"dossier-owner-removed": "Sie wurden <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> ",
|
||||||
"dossier-owner-set": "Sie sind jetzt Besitzer des Dossiers <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>.",
|
"dossier-owner-set": "Sie sind jetzt Besitzer des Dossiers <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>.",
|
||||||
"download-ready": "Ihr <b><a href=\"{downloadHref}\", target=\"_self\">Download</a></b> steht bereit.",
|
"download-ready": "Ihr <b><a href=\"{downloadHref}\", target=\"_self\">Download</a></b> steht bereit.",
|
||||||
"no-data": "Sie haben aktuell keine Benachrichtigungen",
|
"no-data": "Sie haben aktuell keine Benachrichtigungen",
|
||||||
"unassigned-from-file": "Sie wurden von einem Dokument entfernt. <br>Dokument: <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> <br>Dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>",
|
"unassigned-from-file": "Sie wurden von einem Dokument entfernt. <br>Dokument: <b>{fileHref, select, null{{fileName}} other{<a href=\"{fileHref}\" target=\"_blank\">{fileName}</a>}}</b> <br>Dossier: <b>{dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}}}</b>",
|
||||||
"user-becomes-dossier-member": "<b>{user}</b> ist jetzt Mitglied des Dossiers <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
|
"user-becomes-dossier-member": "Sie wurden zu einem Dossier hinzugefügt: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>!",
|
||||||
"user-demoted-to-reviewer": "Sie wurden auf die Reviewer-Rolle heruntergestuft: <b>{dossierHref, select, null{{dossierName}}\n other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>",
|
"user-demoted-to-reviewer": "Sie wurden auf die Reviewer-Rolle heruntergestuft: <b>{dossierHref, select, null{{dossierName}}\n other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>",
|
||||||
"user-promoted-to-approver": "Sie wurden in einem Dossier zum Genehmiger ernannt: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>",
|
"user-promoted-to-approver": "Sie wurden in einem Dossier zum Genehmiger ernannt: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b>",
|
||||||
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
|
"user-removed-as-dossier-member": "Sie wurden als Mitglied entfernt: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> "
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"button-text": "Benachrichtigungen",
|
||||||
|
"deleted-dossier": "Gelöschtes Dossier",
|
||||||
|
"label": "Benachrichtigungen",
|
||||||
|
"mark-all-as-read": "Alle als gelesen markieren",
|
||||||
|
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
|
||||||
},
|
},
|
||||||
"notifications-screen": {
|
"notifications-screen": {
|
||||||
"category": {
|
"category": {
|
||||||
@ -1955,14 +1975,13 @@
|
|||||||
"in-app-notifications": "In-App-Benachrichtigungen"
|
"in-app-notifications": "In-App-Benachrichtigungen"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "Fehler: Aktualisierung der Präferenzen fehlgeschlagen."
|
"generic": "Aktualisierung der Präferenzen fehlgeschlagen."
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"document": "Benachrichtigungen zu Dokumenten",
|
"document": "Benachrichtigungen zu Dokumenten",
|
||||||
"dossier": "Benachrichtigungen zu Dossiers",
|
"dossier": "Benachrichtigungen zu Dossiers",
|
||||||
"other": "Andere Benachrichtigungen"
|
"other": "Andere Benachrichtigungen"
|
||||||
},
|
},
|
||||||
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
|
|
||||||
"options": {
|
"options": {
|
||||||
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin",
|
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin",
|
||||||
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
|
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde",
|
||||||
@ -1980,6 +1999,7 @@
|
|||||||
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
|
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
|
||||||
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
|
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
|
||||||
},
|
},
|
||||||
|
"options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten",
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"daily": "Tägliche Zusammenfassung",
|
"daily": "Tägliche Zusammenfassung",
|
||||||
"instant": "Sofortig",
|
"instant": "Sofortig",
|
||||||
@ -1987,13 +2007,6 @@
|
|||||||
},
|
},
|
||||||
"title": "Benachrichtigungseinstellungen"
|
"title": "Benachrichtigungseinstellungen"
|
||||||
},
|
},
|
||||||
"notifications": {
|
|
||||||
"button-text": "Benachrichtigungen",
|
|
||||||
"deleted-dossier": "Gelöschtes Dossier",
|
|
||||||
"label": "Benachrichtigungen",
|
|
||||||
"mark-all-as-read": "Alle als gelesen markieren",
|
|
||||||
"mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren"
|
|
||||||
},
|
|
||||||
"ocr": {
|
"ocr": {
|
||||||
"confirmation-dialog": {
|
"confirmation-dialog": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
@ -2045,20 +2058,20 @@
|
|||||||
"compare-button": "Vergleichen",
|
"compare-button": "Vergleichen",
|
||||||
"layers-panel-button": "Ebenen",
|
"layers-panel-button": "Ebenen",
|
||||||
"left-panel-button": "Panel",
|
"left-panel-button": "Panel",
|
||||||
"load-all-annotations": "Alle Annotationen geladen",
|
"load-all-annotations": "Alle Annotationen laden",
|
||||||
"no-outlines-text": "Keine Gliederung verfügbar",
|
"no-outlines-text": "Keine Gliederung verfügbar",
|
||||||
"no-signatures-text": "Dieses Dokument enthält keine Unterschriftenfelder.",
|
"no-signatures-text": "Dieses Dokument enthält keine Unterschriftenfelder.",
|
||||||
"outline-multi-select": "Bearbeiten",
|
"outline-multi-select": "Bearbeiten",
|
||||||
"outlines-panel-button": "Gliederung",
|
"outlines-panel-button": "Gliederung",
|
||||||
"pan-tool-button": "Verschieben",
|
"pan-tool-button": "Verschieben",
|
||||||
"rectangle-tool-button": "Bereich schwärzen",
|
"rectangle-tool-button": "Bereich annotieren",
|
||||||
"rotate-left-button": "Seite nach links drehen",
|
"rotate-left-button": "Nach links drehen",
|
||||||
"rotate-right-button": "Seite nach rechts drehen",
|
"rotate-right-button": "Nach rechts drehen",
|
||||||
"select-tool-button": "Auswählen",
|
"select-tool-button": "Auswählen",
|
||||||
"signature-panel-button": "Unterschriften",
|
"signature-panel-button": "Unterschriften",
|
||||||
"thumbnails-panel-button": "Miniaturansicht",
|
"thumbnails-panel-button": "Miniaturansicht",
|
||||||
"toggle-layers": "Layout-Raster {active, select, true{deaktivieren} false{aktivieren} other{}}",
|
"toggle-layers": "Layoutraster {active, select, true{deaktivieren} false{aktivieren} other{}}",
|
||||||
"toggle-readable-redactions": "Schwärzungen {active, select, true{wie im finalen Dokument} false{in Preview-Farbe anzeigen} other{}}",
|
"toggle-readable-redactions": "Show redactions {active, select, true{as in final document} false{in preview color} other{}}",
|
||||||
"toggle-tooltips": "Tooltips für Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}",
|
"toggle-tooltips": "Tooltips für Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}",
|
||||||
"zoom-in-button": "Vergrößern",
|
"zoom-in-button": "Vergrößern",
|
||||||
"zoom-out-button": "Verkleinern"
|
"zoom-out-button": "Verkleinern"
|
||||||
@ -2105,6 +2118,10 @@
|
|||||||
"warnings-label": "Dialoge und Meldungen",
|
"warnings-label": "Dialoge und Meldungen",
|
||||||
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
|
"warnings-subtitle": "„Nicht mehr anzeigen“-Optionen"
|
||||||
},
|
},
|
||||||
|
"processing": {
|
||||||
|
"basic": "Verarbeitung läuft",
|
||||||
|
"ocr": "OCR"
|
||||||
|
},
|
||||||
"processing-status": {
|
"processing-status": {
|
||||||
"ocr": "OCR",
|
"ocr": "OCR",
|
||||||
"pending": "Ausstehend",
|
"pending": "Ausstehend",
|
||||||
@ -2112,10 +2129,6 @@
|
|||||||
"processed": "Verarbeitet",
|
"processed": "Verarbeitet",
|
||||||
"processing": "Verarbeitung läuft"
|
"processing": "Verarbeitung läuft"
|
||||||
},
|
},
|
||||||
"processing": {
|
|
||||||
"basic": "Verarbeitung läuft",
|
|
||||||
"ocr": "OCR"
|
|
||||||
},
|
|
||||||
"readonly": "Lesemodus",
|
"readonly": "Lesemodus",
|
||||||
"readonly-archived": "Lesemodus (archiviert)",
|
"readonly-archived": "Lesemodus (archiviert)",
|
||||||
"redact-text": {
|
"redact-text": {
|
||||||
@ -2132,8 +2145,8 @@
|
|||||||
"legal-basis": "Rechtsgrundlage",
|
"legal-basis": "Rechtsgrundlage",
|
||||||
"options": {
|
"options": {
|
||||||
"in-document": {
|
"in-document": {
|
||||||
"description": "Fügen Sie die Schwärzung an allen Stellen in diesem Dokument hinzu.",
|
"description": "Add redaction for each occurrence of the term in this document.",
|
||||||
"label": "Im Dokument schwärzen"
|
"label": "Redact in document"
|
||||||
},
|
},
|
||||||
"in-dossier": {
|
"in-dossier": {
|
||||||
"description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.",
|
"description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.",
|
||||||
@ -2151,7 +2164,7 @@
|
|||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"type-placeholder": "Typ auswählen...",
|
"type-placeholder": "Typ auswählen...",
|
||||||
"unchanged": "",
|
"unchanged": "",
|
||||||
"value": "Wert"
|
"value": "Value"
|
||||||
},
|
},
|
||||||
"title": "Text schwärzen"
|
"title": "Text schwärzen"
|
||||||
}
|
}
|
||||||
@ -2365,6 +2378,16 @@
|
|||||||
"header": "Größe von {type} ändern"
|
"header": "Größe von {type} ändern"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"revert-manual-changes-dialog": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"save": "Zu den Originalen zurückkehren"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"title": "Manuelle Änderungen"
|
||||||
|
},
|
||||||
|
"title": "Lokale manuelle Änderungen rückgängig machen"
|
||||||
|
},
|
||||||
"revert-value-dialog": {
|
"revert-value-dialog": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
@ -2385,6 +2408,12 @@
|
|||||||
"red-user-admin": "Benutzeradmin",
|
"red-user-admin": "Benutzeradmin",
|
||||||
"regular": "regulärer Benutzer"
|
"regular": "regulärer Benutzer"
|
||||||
},
|
},
|
||||||
|
"search": {
|
||||||
|
"active-dossiers": "Dokumente in aktiven Dossiers",
|
||||||
|
"all-dossiers": "Alle Dokumente",
|
||||||
|
"placeholder": "Dokumente durchsuchen...",
|
||||||
|
"this-dossier": "In diesem Dossier"
|
||||||
|
},
|
||||||
"search-screen": {
|
"search-screen": {
|
||||||
"cols": {
|
"cols": {
|
||||||
"assignee": "Bearbeiter",
|
"assignee": "Bearbeiter",
|
||||||
@ -2408,12 +2437,6 @@
|
|||||||
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
|
"no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.",
|
||||||
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
|
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
|
||||||
},
|
},
|
||||||
"search": {
|
|
||||||
"active-dossiers": "Dokumente in aktiven Dossiers",
|
|
||||||
"all-dossiers": "Alle Dokumente",
|
|
||||||
"placeholder": "Dokumente durchsuchen...",
|
|
||||||
"this-dossier": "In diesem Dossier"
|
|
||||||
},
|
|
||||||
"seconds": "Sekunden",
|
"seconds": "Sekunden",
|
||||||
"size": "Größe",
|
"size": "Größe",
|
||||||
"smtp-auth-config": {
|
"smtp-auth-config": {
|
||||||
@ -2518,7 +2541,7 @@
|
|||||||
"overwrite": "Überschreiben"
|
"overwrite": "Überschreiben"
|
||||||
},
|
},
|
||||||
"question": "Wie möchten Sie vorgehen?",
|
"question": "Wie möchten Sie vorgehen?",
|
||||||
"title": "Das Wörterbuch hat bereits Einträge!"
|
"title": "Das Wörterbuch hat bereits Einträge."
|
||||||
},
|
},
|
||||||
"upload-file": {
|
"upload-file": {
|
||||||
"upload-area-text": "Klicken Sie hier oder ziehen Sie die Datei in diesen Bereich..."
|
"upload-area-text": "Klicken Sie hier oder ziehen Sie die Datei in diesen Bereich..."
|
||||||
|
|||||||
@ -338,6 +338,10 @@
|
|||||||
"error": "Failed to remove annotation: {error}",
|
"error": "Failed to remove annotation: {error}",
|
||||||
"success": "Annotation removed"
|
"success": "Annotation removed"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"error": "Failed to revert manual changes: {error}",
|
||||||
|
"success": "Reverted manual changes successfully."
|
||||||
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -359,6 +363,9 @@
|
|||||||
"resize": {
|
"resize": {
|
||||||
"label": "Resize"
|
"label": "Resize"
|
||||||
},
|
},
|
||||||
|
"revert-changes": {
|
||||||
|
"label": "Revert changes"
|
||||||
|
},
|
||||||
"see-references": {
|
"see-references": {
|
||||||
"label": "See references"
|
"label": "See references"
|
||||||
},
|
},
|
||||||
@ -553,6 +560,9 @@
|
|||||||
"undo": "Undo"
|
"undo": "Undo"
|
||||||
},
|
},
|
||||||
"components": "Components",
|
"components": "Components",
|
||||||
|
"filter": {
|
||||||
|
"search-placeholder": "Search by component..."
|
||||||
|
},
|
||||||
"table-header": {
|
"table-header": {
|
||||||
"component": "Component",
|
"component": "Component",
|
||||||
"value": "Value"
|
"value": "Value"
|
||||||
@ -632,7 +642,7 @@
|
|||||||
"confirm-delete-users": {
|
"confirm-delete-users": {
|
||||||
"cancel": "Keep {usersCount, plural, one{user} other{users}}",
|
"cancel": "Keep {usersCount, plural, one{user} other{users}}",
|
||||||
"delete": "Delete {usersCount, plural, one{user} other{users}}",
|
"delete": "Delete {usersCount, plural, one{user} other{users}}",
|
||||||
"impacted-documents": "All documents pending review from the {usersCount, plural, one{user} other{users}} will be impacted",
|
"impacted-documents": "{documentsCount} {documentsCount, plural, one{document} other{documents}} will be impacted",
|
||||||
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
|
"impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{dossier} other{dossiers}} will be impacted",
|
||||||
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
|
"title": "Delete {usersCount, plural, one{user} other{users}} from workspace",
|
||||||
"toast-error": "Please confirm that you understand the consequences of this action.",
|
"toast-error": "Please confirm that you understand the consequences of this action.",
|
||||||
@ -702,6 +712,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"content": "Reason",
|
"content": "Reason",
|
||||||
|
"copilot": {
|
||||||
|
"label": "Copilot"
|
||||||
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"empty-template": {
|
"empty-template": {
|
||||||
"description": "This template does not contain any dossiers. Create a dossier that applies this ruleset.",
|
"description": "This template does not contain any dossiers. Create a dossier that applies this ruleset.",
|
||||||
@ -1314,7 +1327,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"in-document": {
|
"in-document": {
|
||||||
"description": "",
|
"description": "",
|
||||||
"label": "In Dokument ändern"
|
"label": "Edit in document"
|
||||||
},
|
},
|
||||||
"only-here": {
|
"only-here": {
|
||||||
"description": "",
|
"description": "",
|
||||||
@ -1904,9 +1917,9 @@
|
|||||||
"legalBasis": "Legal Basis",
|
"legalBasis": "Legal Basis",
|
||||||
"options": {
|
"options": {
|
||||||
"multiple-pages": {
|
"multiple-pages": {
|
||||||
"description": "Edit annotation the range of pages",
|
"description": "Add annotation on a range of pages",
|
||||||
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
|
"extraOptionDescription": "Minus(-) for range and comma(,) for enumeration",
|
||||||
"extraOptionLabel": "Range",
|
"extraOptionLabel": "Pages",
|
||||||
"extraOptionPlaceholder": "e.g. 1-20,22,32",
|
"extraOptionPlaceholder": "e.g. 1-20,22,32",
|
||||||
"label": "Apply on multiple pages"
|
"label": "Apply on multiple pages"
|
||||||
},
|
},
|
||||||
@ -1955,7 +1968,7 @@
|
|||||||
"in-app-notifications": "In-app notifications"
|
"in-app-notifications": "In-app notifications"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "Something went wrong... Preferences update failed."
|
"generic": "Preferences update failed."
|
||||||
},
|
},
|
||||||
"groups": {
|
"groups": {
|
||||||
"document": "Document related notifications",
|
"document": "Document related notifications",
|
||||||
@ -2365,6 +2378,16 @@
|
|||||||
"header": "Resize {type}"
|
"header": "Resize {type}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"revert-manual-changes-dialog": {
|
||||||
|
"actions": {
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"save": "Revert to originals"
|
||||||
|
},
|
||||||
|
"details": {
|
||||||
|
"title": "Manual changes"
|
||||||
|
},
|
||||||
|
"title": "Revert local manual changes"
|
||||||
|
},
|
||||||
"revert-value-dialog": {
|
"revert-value-dialog": {
|
||||||
"actions": {
|
"actions": {
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
|||||||
12
apps/red-ui/src/assets/icons/general/revert-changes.svg
Normal file
12
apps/red-ui/src/assets/icons/general/revert-changes.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<svg width="100%" height="100%" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="currentColor" stroke="none">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M10.7389 1.57074C9.61055 1.57074 8.5881 2.02374 7.84324 2.75924L9.09148 2.64397L9.19008 3.71175L5.98676 4.00756L5.69095 0.804226L6.75873 0.705624L6.8882 2.1077C7.84807 1.07713 9.21818 0.431396 10.7389 0.431396C13.6446 0.431396 16 2.78686 16 5.69246C16 8.00114 14.513 9.96249 12.4444 10.6709V9.4459C13.8698 8.79722 14.8607 7.36057 14.8607 5.69246C14.8607 3.4161 13.0153 1.57074 10.7389 1.57074Z"
|
||||||
|
/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||||
|
d="M5.12999 5.91604C5.1117 5.12697 4.46581 4.49243 3.67235 4.49243L3.63795 4.49283L3.60374 4.49402C2.83061 4.52987 2.21432 5.1685 2.21432 5.95044L2.21427 10.5306L2.17777 10.5151C1.55239 10.2584 0.814204 10.4274 0.365254 10.9664L0.340815 10.9966L0.316849 11.0276C-0.153667 11.6531 -0.0961886 12.5351 0.462852 13.0937L1.84534 14.4762L1.84539 14.6236L1.84614 14.652C1.8531 14.7838 1.90846 14.9086 2.00217 15.0023C2.10251 15.1027 2.23862 15.1591 2.38042 15.1591H10.1312L10.1595 15.1583C10.2913 15.1514 10.4162 15.096 10.5099 15.0023C10.6103 14.9019 10.6667 14.7658 10.6667 14.6236V8.53383L10.6661 8.49249C10.6555 8.1211 10.5033 7.76714 10.2397 7.50302C9.96631 7.22968 9.5954 7.07625 9.20863 7.07625L9.1681 7.07682L9.12388 7.07873C8.82966 7.09597 8.54852 7.20237 8.31717 7.38225L8.28601 7.40701L8.25512 7.38227L8.22172 7.35706C7.97441 7.17584 7.674 7.07629 7.36305 7.07629L7.32249 7.07686L7.28024 7.07865C6.98508 7.09547 6.70318 7.20186 6.47141 7.38225L6.44088 7.40668L6.42613 7.3945L6.3935 7.36894C6.04214 7.1019 5.58724 7.01172 5.16238 7.12206L5.13034 7.13076L5.13039 5.95044L5.12999 5.91604ZM3.2854 5.92921C3.29644 5.72504 3.46544 5.56294 3.67237 5.56294C3.88643 5.56294 4.05991 5.73641 4.05991 5.95047V10.1947L4.06043 10.2186C4.07289 10.5033 4.30738 10.7302 4.59493 10.7302C4.89081 10.7302 5.13039 10.4906 5.13039 10.1947V8.53388L5.13096 8.51261C5.142 8.30845 5.311 8.14635 5.51793 8.14635C5.73199 8.14635 5.90547 8.31982 5.90547 8.53388V10.1947L5.90599 10.2186C5.91845 10.5033 6.15294 10.7302 6.44049 10.7302C6.73603 10.7302 6.97552 10.4905 6.97552 10.1947V8.53388L6.97609 8.51261C6.98713 8.30845 7.15613 8.14635 7.36306 8.14635C7.57712 8.14635 7.75059 8.31982 7.75059 8.53388V10.1947L7.75112 10.2186C7.76359 10.5034 7.99817 10.7302 8.28605 10.7302C8.58159 10.7302 8.82108 10.4905 8.82108 10.1947V8.53388L8.82165 8.51261C8.83269 8.30845 9.00169 8.14635 9.20862 8.14635L9.22988 8.14692C9.43405 8.15796 9.59615 8.32696 9.59615 8.53388L9.59611 14.0886H2.88926L2.88027 14.063C2.85363 13.9937 2.81261 13.93 2.75909 13.8765L1.19969 12.3169C1.11077 12.2223 1.06145 12.1002 1.05991 11.9721C1.05863 11.8356 1.11237 11.7041 1.20917 11.6069C1.3059 11.5102 1.43762 11.4565 1.57441 11.4581C1.71139 11.4594 1.8418 11.516 1.93649 11.6149L2.37112 12.0496L2.38985 12.0674C2.54251 12.2061 2.76229 12.2451 2.95431 12.1656C3.15436 12.0829 3.28483 11.8878 3.28483 11.6713V5.95047L3.2854 5.92921Z"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@ -1,3 +1,4 @@
|
|||||||
|
@use 'sass:color';
|
||||||
@use 'common-mixins';
|
@use 'common-mixins';
|
||||||
@use 'variables';
|
@use 'variables';
|
||||||
|
|
||||||
@ -55,8 +56,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ERROR {
|
.ERROR {
|
||||||
stroke: lighten(variables.$primary, 25%);
|
stroke: color.adjust(variables.$primary, $lightness: 25%);
|
||||||
background-color: lighten(variables.$primary, 25%);
|
background-color: color.adjust(variables.$primary, $lightness: 25%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ACTIVE {
|
.ACTIVE {
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
@use 'sass:color';
|
||||||
@use '@angular/material' as mat;
|
@use '@angular/material' as mat;
|
||||||
@use 'variables';
|
@use 'variables';
|
||||||
|
|
||||||
@ -6,8 +7,8 @@
|
|||||||
|
|
||||||
$primary-palette: (
|
$primary-palette: (
|
||||||
default: variables.$primary,
|
default: variables.$primary,
|
||||||
lighter: lighten(variables.$primary, 30%),
|
lighter: color.adjust(variables.$primary, $lightness: 30%),
|
||||||
darker: darken(variables.$primary, 30%),
|
darker: color.adjust(variables.$primary, $lightness: -30%),
|
||||||
text: variables.$primary,
|
text: variables.$primary,
|
||||||
contrast: (
|
contrast: (
|
||||||
default: variables.$light,
|
default: variables.$light,
|
||||||
@ -18,8 +19,8 @@ $primary-palette: (
|
|||||||
|
|
||||||
$secondary-palette: (
|
$secondary-palette: (
|
||||||
default: variables.$accent,
|
default: variables.$accent,
|
||||||
lighter: lighten(variables.$accent, 30%),
|
lighter: color.adjust(variables.$accent, $lightness: 30%),
|
||||||
darker: darken(variables.$accent, 30%),
|
darker: color.adjust(variables.$accent, $lightness: -30%),
|
||||||
text: variables.$accent,
|
text: variables.$accent,
|
||||||
contrast: (
|
contrast: (
|
||||||
default: variables.$light,
|
default: variables.$light,
|
||||||
@ -30,8 +31,8 @@ $secondary-palette: (
|
|||||||
|
|
||||||
$red-palette: (
|
$red-palette: (
|
||||||
default: variables.$primary,
|
default: variables.$primary,
|
||||||
lighter: lighten(variables.$primary, 30%),
|
lighter: color.adjust(variables.$primary, $lightness: 30%),
|
||||||
darker: darken(variables.$primary, 30%),
|
darker: color.adjust(variables.$primary, $lightness: -30%),
|
||||||
text: variables.$primary,
|
text: variables.$primary,
|
||||||
contrast: (
|
contrast: (
|
||||||
default: variables.$light,
|
default: variables.$light,
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
@use 'sass:color';
|
||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
@use 'variables' as vars;
|
@use 'variables' as vars;
|
||||||
@use 'common-functions';
|
@use 'common-functions';
|
||||||
@ -109,12 +110,12 @@
|
|||||||
$iqser-app-name-color: vars.$accent
|
$iqser-app-name-color: vars.$accent
|
||||||
);
|
);
|
||||||
|
|
||||||
$light-accent-5: lighten(vars.$accent, 5%);
|
$light-accent-5: color.adjust(vars.$accent, $lightness: 5%);
|
||||||
$light-accent-10: lighten(vars.$accent, 10%);
|
$light-accent-10: color.adjust(vars.$accent, $lightness: 10%);
|
||||||
|
|
||||||
$dark-accent-5: darken(vars.$accent, 5%);
|
$dark-accent-5: color.adjust(vars.$accent, $lightness: -5%);
|
||||||
$dark-accent-8: darken(vars.$accent, 8%);
|
$dark-accent-8: color.adjust(vars.$accent, $lightness: -8%);
|
||||||
$dark-accent-10: darken(vars.$accent, 10%);
|
$dark-accent-10: color.adjust(vars.$accent, $lightness: -10%);
|
||||||
|
|
||||||
@include common-variables.configureDark(
|
@include common-variables.configureDark(
|
||||||
$iqser-primary: vars.$primary,
|
$iqser-primary: vars.$primary,
|
||||||
@ -172,7 +173,8 @@ body {
|
|||||||
--documine-viewer-width: calc(
|
--documine-viewer-width: calc(
|
||||||
100% - var(--structured-component-management-width) - calc(var(--documine-workload-content-width) - 55px) - var(
|
100% - var(--structured-component-management-width) - calc(var(--documine-workload-content-width) - 55px) - var(
|
||||||
--quick-navigation-width
|
--quick-navigation-width
|
||||||
) - 3px
|
) -
|
||||||
|
3px
|
||||||
);
|
);
|
||||||
--viewer-height: calc(100% - calc(var(--iqser-top-bar-height) + 50px));
|
--viewer-height: calc(100% - calc(var(--iqser-top-bar-height) + 50px));
|
||||||
}
|
}
|
||||||
@ -198,7 +200,8 @@ body {
|
|||||||
width: calc(
|
width: calc(
|
||||||
100% - var(--structured-component-management-width) - calc(var(--documine-workload-content-width) - 55px) - var(
|
100% - var(--structured-component-management-width) - calc(var(--documine-workload-content-width) - 55px) - var(
|
||||||
--workload-width
|
--workload-width
|
||||||
) - 3px
|
) -
|
||||||
|
3px
|
||||||
);
|
);
|
||||||
right: calc(var(--workload-width) + 1px);
|
right: calc(var(--workload-width) + 1px);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,7 +46,7 @@ echo '{
|
|||||||
"AVAILABLE_NOTIFICATIONS_DAYS":"'"$AVAILABLE_NOTIFICATIONS_DAYS"'",
|
"AVAILABLE_NOTIFICATIONS_DAYS":"'"$AVAILABLE_NOTIFICATIONS_DAYS"'",
|
||||||
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES":"'"$AVAILABLE_OLD_NOTIFICATIONS_MINUTES"'",
|
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES":"'"$AVAILABLE_OLD_NOTIFICATIONS_MINUTES"'",
|
||||||
"NOTIFICATIONS_THRESHOLD":"'"$NOTIFICATIONS_THRESHOLD"'",
|
"NOTIFICATIONS_THRESHOLD":"'"$NOTIFICATIONS_THRESHOLD"'",
|
||||||
"WATERMARK_PREVIEW_PAPER_FORMAT":"'"$WATERMARK_PREVIEW_PAPER_FORMAT"'"
|
"WATERMARK_PREVIEW_PAPER_FORMAT":"'"$WATERMARK_PREVIEW_PAPER_FORMAT"'",
|
||||||
"LANDING_PAGE_THEME":"'"$LANDING_PAGE_THEME"'"
|
"LANDING_PAGE_THEME":"'"$LANDING_PAGE_THEME"'"
|
||||||
}' >/usr/share/nginx/html/ui/assets/config/config.json
|
}' >/usr/share/nginx/html/ui/assets/config/config.json
|
||||||
|
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 7bb24c4f919faa2525c728315c4f679b1af45842
|
Subproject commit 7f13fa62d3d2b346c609bc4978cff75f37f1ee6b
|
||||||
@ -22,6 +22,7 @@
|
|||||||
"style": "kebab-case"
|
"style": "kebab-case"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"@angular-eslint/no-host-metadata-property": "off",
|
||||||
"@typescript-eslint/unbound-method": "error",
|
"@typescript-eslint/unbound-method": "error",
|
||||||
"@typescript-eslint/no-floating-promises": "off",
|
"@typescript-eslint/no-floating-promises": "off",
|
||||||
"@typescript-eslint/naming-convention": [
|
"@typescript-eslint/naming-convention": [
|
||||||
|
|||||||
@ -31,3 +31,4 @@ export * from './lib/colors';
|
|||||||
export * from './lib/component-log';
|
export * from './lib/component-log';
|
||||||
export * from './lib/component-mappings';
|
export * from './lib/component-mappings';
|
||||||
export * from './lib/component-definitions';
|
export * from './lib/component-definitions';
|
||||||
|
export * from './lib/web-socket';
|
||||||
|
|||||||
@ -10,7 +10,8 @@ export type ManualRedactionActions =
|
|||||||
| 'undo'
|
| 'undo'
|
||||||
| 'force-redaction'
|
| 'force-redaction'
|
||||||
| 'force-hint'
|
| 'force-hint'
|
||||||
| 'recategorize-annotation';
|
| 'recategorize-annotation'
|
||||||
|
| 'revert-changes';
|
||||||
|
|
||||||
export const AnnotationIconTypes = {
|
export const AnnotationIconTypes = {
|
||||||
square: 'square',
|
square: 'square',
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
|
|||||||
ProcessingFileStatuses.FULL_PROCESSING,
|
ProcessingFileStatuses.FULL_PROCESSING,
|
||||||
ProcessingFileStatuses.PRE_PROCESSING,
|
ProcessingFileStatuses.PRE_PROCESSING,
|
||||||
ProcessingFileStatuses.TABLE_PARSING_ANALYZING,
|
ProcessingFileStatuses.TABLE_PARSING_ANALYZING,
|
||||||
|
ProcessingFileStatuses.OCR_PROCESSING_QUEUED,
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const isFullProcessingStatuses: List<ProcessingFileStatus> = [
|
export const isFullProcessingStatuses: List<ProcessingFileStatus> = [
|
||||||
|
|||||||
@ -19,9 +19,9 @@ export class TrashDossier extends TrashItem implements Partial<IDossier> {
|
|||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
dossier: IDossier,
|
dossier: IDossier,
|
||||||
protected readonly _retentionHours: number,
|
protected override readonly _retentionHours: number,
|
||||||
readonly hasRestoreRights: boolean,
|
override readonly hasRestoreRights: boolean,
|
||||||
readonly hasHardDeleteRights: boolean,
|
override readonly hasHardDeleteRights: boolean,
|
||||||
readonly ownerName: string,
|
readonly ownerName: string,
|
||||||
) {
|
) {
|
||||||
super(_retentionHours, dossier.softDeletedTime || '-', hasRestoreRights, hasHardDeleteRights);
|
super(_retentionHours, dossier.softDeletedTime || '-', hasRestoreRights, hasHardDeleteRights);
|
||||||
|
|||||||
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