Merge branch 'master' into VM/RED-10557

This commit is contained in:
Valentin Mihai 2024-12-05 12:42:04 +02:00
commit 402be3dadf
50 changed files with 397 additions and 175 deletions

View File

@ -4,21 +4,21 @@ import { UserPreferenceService } from '@users/user-preference.service';
import { getConfig } from '@iqser/common-ui';
import { AppConfig } from '@red/domain';
import { NavigationEnd, Router } from '@angular/router';
import { filter, map, switchMap, take } from 'rxjs/operators';
import { filter, map, switchMap, take, tap } from 'rxjs/operators';
import { APP_TYPE_PATHS } from '@common-ui/utils/constants';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { TenantsService } from '@common-ui/tenants';
function loadCustomTheme() {
const cssFileName = getConfig<AppConfig>().THEME;
if (cssFileName) {
const head = document.getElementsByTagName('head')[0];
const link = document.createElement('link');
link.id = cssFileName;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'assets/styles/themes/' + cssFileName + '.css';
link.media = 'all';
head.appendChild(link);
}
export function loadCustomTheme(cssFileName: string) {
const head = document.getElementsByTagName('head')[0];
const link = document.createElement('link');
link.id = cssFileName;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'assets/styles/themes/' + cssFileName + '.css';
link.media = 'all';
head.appendChild(link);
}
@Component({
@ -34,9 +34,12 @@ export class AppComponent {
userPreferenceService: UserPreferenceService,
renderer: Renderer2,
private readonly _router: Router,
private readonly _iconRegistry: MatIconRegistry,
private readonly _sanitizer: DomSanitizer,
private readonly _tenantsService: TenantsService,
) {
const config = getConfig<AppConfig>();
renderer.addClass(document.body, userPreferenceService.getTheme());
loadCustomTheme();
const removeQueryParams = _router.events.pipe(
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
@ -47,9 +50,25 @@ export class AppComponent {
);
removeQueryParams.subscribe();
if (getConfig().IS_DOCUMINE) {
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
}
this._tenantsService
.waitForSettingTenant()
.pipe(
tap(() => {
const isDocumine = this._tenantsService.activeTenant.documine;
const logo = isDocumine ? 'documine' : 'redaction';
_iconRegistry.addSvgIconInNamespace(
'iqser',
'logo',
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}-logo.svg`),
);
if (isDocumine) {
document.getElementById('favicon').setAttribute('href', 'assets/icons/documine-logo.ico');
}
loadCustomTheme(isDocumine ? APP_TYPE_PATHS.SCM : APP_TYPE_PATHS.REDACT);
}),
take(1),
)
.subscribe();
}
#removeKeycloakQueryParams() {

View File

@ -117,7 +117,7 @@ export const appModuleFactory = (config: AppConfig) => {
resetTimeoutOnDuplicate: true,
}),
TenantsModule.forRoot(),
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY || '/assets/i18n/redact/' }),
IqserTranslateModule.forRoot({ pathPrefix: config.BASE_TRANSLATIONS_DIRECTORY }),
IqserLoadingModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
LoggerModule.forRoot(undefined, {

View File

@ -32,7 +32,7 @@
[formControlName]="role"
color="primary"
>
{{ translations[role] | translate }}
{{ translations[role] | translate: { count: 1 } }}
</mat-checkbox>
</div>
</div>

View File

@ -3,10 +3,8 @@ import { ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup, Validators }
import { AdminDialogService } from '../../../services/admin-dialog.service';
import { BaseFormComponent, IconButtonComponent, LoadingService, Toaster } from '@iqser/common-ui';
import { rolesTranslations } from '@translations/roles-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { User } from '@red/domain';
import { UserService } from '@users/user.service';
import { HttpStatusCode } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
import { IProfileUpdateRequest } from '@iqser/common-ui/lib/users';
import { TranslateModule } from '@ngx-translate/core';

View File

@ -126,7 +126,7 @@ export class UserListingScreenComponent extends ListingComponent<User> implement
getDisplayRoles(user: User) {
const oldRedRoles = user.roles.filter(role => role.startsWith('RED_'));
const translatedRoles = oldRedRoles.map(role => this._translateService.instant(this.translations[role]));
const translatedRoles = oldRedRoles.map(role => this._translateService.instant(this.translations[role], { count: 1 }));
return translatedRoles.join(', ') || this._translateService.instant(this.translations['NO_ROLE']);
}

View File

@ -26,6 +26,7 @@ export class DossierOverviewBulkActionsComponent {
readonly maxWidth = input<number>();
readonly buttons = computed(() => this.#buttons);
readonly IqserTooltipPositions = IqserTooltipPositions;
readonly #areFilesInErrorState = computed(() => this.selectedFiles().some(file => file.isError));
readonly #areRulesLocked = computed(() => this._rulesService.currentTemplateRules().timeoutDetected);
readonly #allFilesAreUnderReviewOrUnassigned = computed(() =>
this.selectedFiles().reduce((acc, file) => acc && (file.isUnderReview || file.isNew), true),
@ -47,18 +48,34 @@ export class DossierOverviewBulkActionsComponent {
);
readonly #canDelete = computed(() => this._permissionsService.canSoftDeleteFile(this.selectedFiles(), this.dossier()));
readonly #canReanalyse = computed(() => this._permissionsService.canReanalyseFile(this.selectedFiles(), this.dossier()));
readonly #canDisableAutoAnalysis = computed(() =>
this._permissionsService.canDisableAutoAnalysis(this.selectedFiles(), this.dossier()),
readonly #canDisableAutoAnalysis = computed(
() => this._permissionsService.canDisableAutoAnalysis(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canEnableAutoAnalysis = computed(
() => this._permissionsService.canEnableAutoAnalysis(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canEnableAutoAnalysis = computed(() => this._permissionsService.canEnableAutoAnalysis(this.selectedFiles(), this.dossier()));
readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.selectedFiles(), this.dossier()));
readonly #canOcr = computed(() => this._permissionsService.canOcrFile(this.selectedFiles(), this.dossier()));
readonly #canSetToNew = computed(() => this._permissionsService.canSetToNew(this.selectedFiles(), this.dossier()));
readonly #canSetToUnderReview = computed(() => this._permissionsService.canSetUnderReview(this.selectedFiles(), this.dossier()));
readonly #canSetToUnderApproval = computed(() => this._permissionsService.canSetUnderApproval(this.selectedFiles(), this.dossier()));
readonly #isReadyForApproval = computed(() => this._permissionsService.isReadyForApproval(this.selectedFiles(), this.dossier()));
readonly #canApprove = computed(() => this._permissionsService.canBeApproved(this.selectedFiles(), this.dossier()));
readonly #canUndoApproval = computed(() => this._permissionsService.canUndoApproval(this.selectedFiles(), this.dossier()));
readonly #canOcr = computed(
() => this._permissionsService.canOcrFile(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canSetToNew = computed(
() => this._permissionsService.canSetToNew(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canSetToUnderReview = computed(
() => this._permissionsService.canSetUnderReview(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canSetToUnderApproval = computed(
() => this._permissionsService.canSetUnderApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #isReadyForApproval = computed(
() => this._permissionsService.isReadyForApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canApprove = computed(
() => this._permissionsService.canBeApproved(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #canUndoApproval = computed(
() => this._permissionsService.canUndoApproval(this.selectedFiles(), this.dossier()) && !this.#areFilesInErrorState(),
);
readonly #assignTooltip = computed(() =>
this.#allFilesAreUnderApproval() ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'),
);

View File

@ -126,7 +126,7 @@ export class AnnotationActionsComponent {
async acceptRecommendation(): Promise<void> {
const annotations = untracked(this.annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept');
}
hideAnnotation() {

View File

@ -89,7 +89,7 @@ export class AnnotationDetailsComponent {
icon: 'red:rule',
description: _('annotation-engines.rule'),
show: isBasedOn(annotation, LogEntryEngines.RULE),
translateParams: { rule: annotation.legalBasisValue || '' },
translateParams: { rule: annotation.legalBasisValue === 'n-a' ? '' : annotation.legalBasisValue || '' },
},
{
icon: 'red:import_redactions',

View File

@ -16,6 +16,7 @@ import { HighlightsSeparatorComponent } from '../highlights-separator/highlights
import { AnnotationWrapperComponent } from '../annotation-wrapper/annotation-wrapper.component';
import { AnnotationReferencesListComponent } from '../annotation-references-list/annotation-references-list.component';
import { Clipboard } from '@angular/cdk/clipboard';
import { isTargetInput } from '@utils/functions';
@Component({
selector: 'redaction-annotations-list',
@ -68,7 +69,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
console.log('Selected Annotation:', annotation);
}
if (($event?.target as IqserEventTarget)?.localName === 'input') {
if (isTargetInput($event)) {
return;
}

View File

@ -5,12 +5,13 @@ import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import { ComponentLogService } from '@services/entity-services/component-log.service';
import { StopPropagationDirective } from '@iqser/common-ui';
@Component({
selector: 'redaction-documine-export',
templateUrl: './documine-export.component.html',
standalone: true,
imports: [MatTooltip, TranslateModule, MatMenuTrigger, MatMenu, MatMenuItem],
imports: [MatTooltip, TranslateModule, MatMenuTrigger, MatMenu, MatMenuItem, StopPropagationDirective],
})
export class DocumineExportComponent {
readonly dossier = input<Dossier>();

View File

@ -20,6 +20,12 @@
display: flex;
flex-direction: column;
gap: 10px;
span {
word-wrap: break-word;
word-break: break-word;
text-overflow: ellipsis;
}
}
.actions {

View File

@ -4,7 +4,6 @@ import {
Component,
computed,
ElementRef,
HostListener,
Input,
NgZone,
OnDestroy,
@ -50,6 +49,7 @@ import { ALL_HOTKEYS } from '../../utils/constants';
import { AnnotationDrawService } from '../../../pdf-viewer/services/annotation-draw.service';
import { FileManagementService } from '@services/files/file-management.service';
import { MatDialog } from '@angular/material/dialog';
import { isTargetInput, isTargetTextArea } from '@utils/functions';
@Component({
selector: 'redaction-file-header',
@ -210,29 +210,26 @@ export class FileHeaderComponent implements OnInit, AfterViewInit, OnDetach, OnD
this._changeRef.markForCheck();
}
if ($event.key === 'F5') {
window.location.reload();
}
if (isTargetInput($event) || isTargetTextArea($event)) {
return;
}
if (!$event.ctrlKey && !$event.metaKey && ['f', 'F'].includes($event.key)) {
// if you type in an input, don't toggle full-screen
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
return;
}
this.toggleFullScreen();
return;
}
if (['h', 'H'].includes($event.key)) {
if ($event.target instanceof HTMLInputElement || $event.target instanceof HTMLTextAreaElement) {
return;
}
this._ngZone.run(() => {
window.focus();
this._helpModeService.activateHelpMode(false);
});
return;
}
if ($event.key === 'F5') {
window.location.reload();
}
}
#openFullScreen() {

View File

@ -59,7 +59,7 @@ import { PageExclusionComponent } from '../page-exclusion/page-exclusion.compone
import { PagesComponent } from '../pages/pages.component';
import { ReadonlyBannerComponent } from '../readonly-banner/readonly-banner.component';
import { DocumentInfoComponent } from '../document-info/document-info.component';
import { getLast } from '@utils/functions';
import { getLast, isTargetInput } from '@utils/functions';
import { ALL_ANNOTATIONS_PAGE } from '../../utils/constants';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
@ -251,11 +251,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
handleKeyEvent($event: KeyboardEvent): void {
const multiSelectServiceInactive = untracked(this.multiSelectService.inactive);
if (
!ALL_HOTKEY_ARRAY.includes($event.key) ||
this._dialog.openDialogs.length ||
($event.target as IqserEventTarget).localName === 'input'
) {
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this._dialog.openDialogs.length || isTargetInput($event)) {
return;
}
@ -342,7 +338,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
}
preventKeyDefault($event: KeyboardEvent): void {
if (COMMAND_KEY_ARRAY.includes($event.key) && !(($event.target as any).localName === 'input')) {
if (COMMAND_KEY_ARRAY.includes($event.key) && !isTargetInput($event)) {
$event.preventDefault();
}
}

View File

@ -1,24 +1,35 @@
<div *ngIf="_state.file() as file" class="justify-center banner read-only flex">
<div *ngIf="file.isFullProcessing" class="ocr-indicator">
<ng-container *ngIf="file.isOcrProcessing; else defaultProcessing">
<redaction-ocr-progress-bar
[numberOfOCRedPages]="file.numberOfOCRedPages"
[numberOfPagesToOCR]="file.numberOfPagesToOCR"
[showLabel]="true"
></redaction-ocr-progress-bar>
</ng-container>
@if (_state.file(); as file) {
<div class="justify-center banner read-only flex">
@if (file.isFullProcessing) {
<div class="ocr-indicator">
@if (file.isOcrProcessing) {
<redaction-ocr-progress-bar
[numberOfOCRedPages]="file.numberOfOCRedPages"
[numberOfPagesToOCR]="file.numberOfPagesToOCR"
[showLabel]="true"
></redaction-ocr-progress-bar>
} @else {
<span [translate]="'processing.basic'" class="read-only-text"></span>
<mat-progress-bar class="white ml-8 w-100" mode="indeterminate"></mat-progress-bar>
}
</div>
}
<ng-template #defaultProcessing>
<span [translate]="'processing.basic'" class="read-only-text"></span>
<mat-progress-bar class="white ml-8 w-100" mode="indeterminate"></mat-progress-bar>
</ng-template>
<div class="flex-center">
@if (!customTranslation) {
<mat-icon
[matTooltip]="file.isFullProcessing ? ('readonly' | translate) : null"
[matTooltipPosition]="'above'"
class="primary-white"
svgIcon="red:read-only"
></mat-icon>
@if (!file.isFullProcessing) {
<span [translate]="_state.dossier().isActive ? 'readonly' : 'readonly-archived'" class="read-only-text"></span>
}
}
@if (customTranslation) {
<span [translate]="customTranslation" class="read-only-text"></span>
}
</div>
</div>
<div class="flex-center">
<ng-container *ngIf="!customTranslation">
<mat-icon class="primary-white" svgIcon="red:read-only"></mat-icon>
<span [translate]="_state.dossier().isActive ? 'readonly' : 'readonly-archived'" class="read-only-text"></span>
</ng-container>
<span *ngIf="customTranslation" [translate]="customTranslation" class="read-only-text"></span>
</div>
</div>
}

View File

@ -22,7 +22,7 @@
.ocr-indicator {
display: flex;
flex: 1;
margin-right: 5px;
align-items: center;
}

View File

@ -5,13 +5,14 @@ import { NgIf } from '@angular/common';
import { MatProgressBar } from '@angular/material/progress-bar';
import { MatIcon } from '@angular/material/icon';
import { TranslateModule } from '@ngx-translate/core';
import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'redaction-readonly-banner',
templateUrl: './readonly-banner.component.html',
styleUrls: ['./readonly-banner.component.scss'],
standalone: true,
imports: [OcrProgressBarComponent, NgIf, MatProgressBar, MatIcon, TranslateModule],
imports: [OcrProgressBarComponent, NgIf, MatProgressBar, MatIcon, TranslateModule, MatTooltip],
})
export class ReadonlyBannerComponent {
protected readonly _state = inject(FilePreviewStateService);

View File

@ -1,10 +1,12 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div
[translateParams]="{ type: isImage ? 'image' : isHint ? 'hint' : 'redaction' }"
[translate]="'edit-redaction.dialog.title'"
class="dialog-header heading-l"
></div>
<div class="dialog-header heading-l">
<span
[translateParams]="{ type: isImage ? 'image' : isHint ? 'hint' : 'redaction' }"
[translate]="'edit-redaction.dialog.title'"
[attr.help-mode-key]="helpModeKeyByType"
></span>
</div>
<div [class.image-dialog]="isRedacted && isImage" [class.rectangle-dialog]="allRectangles" class="dialog-content redaction">
<div *ngIf="!isImage && redactedTexts.length && !allRectangles" class="iqser-input-group">

View File

@ -39,6 +39,7 @@ import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-r
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { validatePageRange } from '../../utils/form-validators';
import { parseRectanglePosition, parseSelectedPageNumbers, prefillPageRange } from '../../utils/enhance-manual-redaction-request.utils';
import { ActionsHelpModeKeys } from '../../utils/constants';
interface TypeSelectOptions {
type: string;
@ -83,12 +84,19 @@ export class EditRedactionDialogComponent
readonly isHint = this.annotations.every(annotation => annotation.HINT || annotation.IMAGE_HINT);
readonly isRedacted = this.annotations.every(annotation => annotation.isRedacted);
readonly isImported: boolean = this.annotations.every(annotation => annotation.imported || annotation.type === 'imported_redaction');
readonly isSkipped: boolean = this.annotations.every(annotation => annotation.isSkipped);
readonly allRectangles = this.annotations.reduce((acc, a) => acc && a.AREA, true);
readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }];
readonly tableData: ValueColumn[][] = this.data.annotations.map(redaction => [
{ label: redaction.value, bold: true },
{ label: redaction.typeLabel },
]);
readonly annotationsType = this.isHint
? ActionsHelpModeKeys.hint
: this.isSkipped
? ActionsHelpModeKeys.skipped
: ActionsHelpModeKeys.redaction;
readonly helpModeKeyByType = `${this.annotationsType}_edit_DIALOG`;
options = this.allRectangles ? getRectangleRedactOptions('edit', this.data.annotations) : getEditRedactionOptions(this.isHint);
legalOptions: LegalBasisOption[] = [];
dictionaries: Dictionary[] = [];

View File

@ -1,6 +1,11 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div class="dialog-header heading-l" [translate]="dialogTitle"></div>
<div class="dialog-header heading-l" [translate]="dialogTitle">
<span
[translate]="dialogTitle"
[attr.help-mode-key]="isSkipped ? 'skipped_force_DIALOG' : isImageHint ? 'hint_redact_DIALOG' : ''"
></span>
</div>
<div class="dialog-content force-annotation">
@if (!isImageHint) {

View File

@ -10,9 +10,8 @@ import {
IqserDialogComponent,
} from '@iqser/common-ui';
import { JustificationsService } from '@services/entity-services/justifications.service';
import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
import { ILegalBasisChangeRequest } from '@red/domain';
import { firstValueFrom } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Roles } from '@users/roles';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import {
@ -38,10 +37,8 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.';
standalone: true,
imports: [
ReactiveFormsModule,
NgIf,
SelectedAnnotationsTableComponent,
MatFormField,
MatSelectTrigger,
MatSelect,
MatOption,
MatTooltip,
@ -50,7 +47,6 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.';
IconButtonComponent,
IqserDenyDirective,
CircleButtonComponent,
NgForOf,
HelpButtonComponent,
DetailsRadioComponent,
],
@ -68,6 +64,7 @@ export class ForceAnnotationDialogComponent
{ label: redaction.value, bold: true },
{ label: redaction.typeLabel },
]);
readonly isSkipped = this.data.annotations.every(annotation => annotation.isSkipped);
legalOptions: LegalBasisOption[] = [];
protected readonly roles = Roles;

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'redact-text.dialog.title'" [attr.help-mode-key]="helpModeKey"></span>
</div>
<div class="dialog-content redaction">
<div class="iqser-input-group">
@ -59,6 +61,7 @@
</iqser-icon-button>
<div [translate]="'redact-text.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

@ -8,7 +8,15 @@ import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select
import { MatTooltip } from '@angular/material/tooltip';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import {
CircleButtonComponent,
HasScrollbarDirective,
HelpButtonComponent,
IconButtonComponent,
IconButtonTypes,
IqserDenyDirective,
IqserDialogComponent,
} from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { TranslateModule } from '@ngx-translate/core';
import { Dictionary, IAddRedactionRequest, SuperTypes } from '@red/domain';
@ -30,6 +38,7 @@ import {
RedactRecommendationResult,
ResizeOptions,
} from '../../utils/dialog-types';
import { Roles } from '@users/roles';
@Component({
templateUrl: './redact-recommendation-dialog.component.html',
@ -51,6 +60,8 @@ import {
MatDialogClose,
MatSelectTrigger,
MatSelect,
HelpButtonComponent,
IqserDenyDirective,
],
})
export class RedactRecommendationDialogComponent
@ -75,6 +86,8 @@ export class RedactRecommendationDialogComponent
reason: [null],
});
readonly helpModeKey = `recommendation_${this.data.action}_DIALOG`;
readonly tableColumns: ValueColumn[] = [{ label: 'Value' }, { label: 'Type' }];
readonly tableData: ValueColumn[][] = this.data.annotations.map(redaction => [
{ label: redaction.value, bold: true },
@ -204,4 +217,6 @@ export class RedactRecommendationDialogComponent
}
this.form.controls.dictionary.setValue(this.#manualRedactionTypeExists ? SuperTypes.ManualRedaction : null);
}
protected readonly roles = Roles;
}

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'redact-text.dialog.title'" [attr.help-mode-key]="'add_redaction_DIALOG'"></span>
</div>
<div class="dialog-content redaction">
<div class="iqser-input-group w-full selected-text-group">
@ -134,6 +136,7 @@
/>
<div [translate]="'redact-text.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

@ -8,7 +8,15 @@ import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select
import { MatTooltip } from '@angular/material/tooltip';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import {
CircleButtonComponent,
HasScrollbarDirective,
HelpButtonComponent,
IconButtonComponent,
IconButtonTypes,
IqserDenyDirective,
IqserDialogComponent,
} from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { Dictionary, SuperTypes } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
@ -55,6 +63,8 @@ const MAXIMUM_TEXT_AREA_WIDTH = 421;
AsyncPipe,
IconButtonComponent,
MatDialogClose,
HelpButtonComponent,
IqserDenyDirective,
],
})
export class RedactTextDialogComponent
@ -138,7 +148,7 @@ export class RedactTextDialogComponent
get applyToAll() {
return this.isSystemDefault || this._userPreferences.getAddRedactionDefaultExtraOption() === 'undefined'
? this.data.applyToAllDossiers ?? true
? (this.data.applyToAllDossiers ?? true)
: stringToBoolean(this._userPreferences.getAddRedactionDefaultExtraOption());
}

View File

@ -1,9 +1,13 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div
[innerHTML]="(isBulk ? 'remove-redaction.dialog.title-bulk' : 'remove-redaction.dialog.title') | translate: typeTranslationArg"
class="dialog-header heading-l"
></div>
<div class="dialog-header heading-l">
<span
[innerHTML]="
(isBulk ? 'remove-redaction.dialog.title-bulk' : 'remove-redaction.dialog.title') | translate: typeTranslationArg
"
[attr.help-mode-key]="helpModeKeyByType"
></span>
</div>
<div [ngStyle]="{ height: dialogContentHeight + redactedTextsAreaHeight + 'px' }" class="dialog-content redaction">
<div class="iqser-input-group">

View File

@ -76,6 +76,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
: this.recommendation
? ANNOTATION_TYPES.RECOMMENDATION
: ANNOTATION_TYPES.REDACTION;
readonly helpModeKeyByType = `${this.annotationsType}_remove_DIALOG`;
readonly optionByType = {
recommendation: {
main: this._userPreferences.getRemoveRecommendationDefaultOption(),

View File

@ -1,6 +1,11 @@
<section class="dialog">
<form (submit)="save()" [formGroup]="form">
<div [innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span
[innerHTML]="'resize-redaction.dialog.header' | translate: { type: dialogHeaderType }"
[attr.help-mode-key]="dialogTitleHelpKey"
></span>
</div>
<div class="dialog-content redaction">
<ng-container *ngIf="!redaction.isImage && !redaction.AREA">
@ -48,6 +53,7 @@
></iqser-icon-button>
<div [translate]="'resize-redaction.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
<iqser-help-button *deny="roles.getRss"></iqser-help-button>
</div>
</form>

View File

@ -6,12 +6,21 @@ import { MatFormField } from '@angular/material/form-field';
import { MatOption, MatSelect, MatSelectTrigger } from '@angular/material/select';
import { DetailsRadioOption } from '@common-ui/inputs/details-radio/details-radio-option';
import { DetailsRadioComponent } from '@common-ui/inputs/details-radio/details-radio.component';
import { CircleButtonComponent, HasScrollbarDirective, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
import {
CircleButtonComponent,
HasScrollbarDirective,
HelpButtonComponent,
IconButtonComponent,
IconButtonTypes,
IqserDenyDirective,
IqserDialogComponent,
} from '@iqser/common-ui';
import { TranslateModule } from '@ngx-translate/core';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { getResizeRedactionOptions } from '../../utils/dialog-options';
import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedactionResult } from '../../utils/dialog-types';
import { Roles } from '@users/roles';
@Component({
templateUrl: './resize-redaction-dialog.component.html',
@ -30,6 +39,8 @@ import { ResizeOptions, ResizeRedactionData, ResizeRedactionOption, ResizeRedact
HasScrollbarDirective,
MatDialogClose,
NgIf,
HelpButtonComponent,
IqserDenyDirective,
],
})
export class ResizeRedactionDialogComponent extends IqserDialogComponent<
@ -67,6 +78,14 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
return this.data.redaction.HINT ? 'hint' : this.data.redaction.isSkippedImageHint ? 'image' : 'redaction';
}
get dialogTitleHelpKey() {
return this.data.redaction.isRecommendation
? 'recommendation_resize_DIALOG'
: this.data.redaction.isHint
? 'hint_resize_DIALOG'
: 'redaction_resize_DIALOG';
}
get displayedDictionaryLabel() {
const dictType = this.form.get('dictionary').value;
if (dictType) {
@ -93,4 +112,6 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
option: this.options[0],
});
}
protected readonly roles = Roles;
}

View File

@ -295,7 +295,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
super.ngOnDestroy();
}
@Bind()
handleEscInsideViewer($event: KeyboardEvent) {
$event.preventDefault();
if (!!this._annotationManager.selected[0]) {
@ -369,7 +368,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.pdfProxyService.configureElements();
this.#restoreOldFilters();
this.pdf.instance.UI.hotkeys.on('esc', this.handleEscInsideViewer);
this.pdf.instance.UI.hotkeys.on('esc', {
keydown: (e: KeyboardEvent) => this.pdf.escKeyHandler.keydown(e),
keyup: (e: KeyboardEvent) => {
this.pdf.escKeyHandler.keyup(e);
this.handleEscInsideViewer(e);
},
});
this._viewerHeaderService.resetLayers();
this.pdf.instance.UI.iframeWindow.document.removeEventListener('click', this.handleViewerClick);
this.pdf.instance.UI.iframeWindow.document.addEventListener('click', this.handleViewerClick);

View File

@ -225,9 +225,9 @@ export class AnnotationActionsService {
this.#processObsAndEmit(request$).then();
}
async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[]) {
async convertRecommendationToAnnotation(recommendations: AnnotationWrapper[], action: 'accept' | 'resize') {
const { dossierId, fileId } = this._state;
const data = this.#getRedactRecommendationDialogData(recommendations) as RedactRecommendationData;
const data = this.#getRedactRecommendationDialogData(recommendations, action) as RedactRecommendationData;
const dialog = this._iqserDialog.openDefault(RedactRecommendationDialogComponent, { data });
const result = await dialog.result();
if (!result) {
@ -277,7 +277,7 @@ export class AnnotationActionsService {
recommendation.isRemoved = true;
await this._annotationDrawService.draw([recommendation], this._skippedService.hideSkipped(), this._state.dossierTemplateId);
return this.convertRecommendationToAnnotation([recommendation]);
return this.convertRecommendationToAnnotation([recommendation], 'resize');
}
const dossier = this._state.dossier();
@ -550,7 +550,7 @@ export class AnnotationActionsService {
return this._iqserDialog.openDefault(EditRedactionDialogComponent, { data });
}
#getRedactRecommendationDialogData(annotations: AnnotationWrapper[]) {
#getRedactRecommendationDialogData(annotations: AnnotationWrapper[], action: 'accept' | 'resize') {
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
const isApprover = this._permissionsService.isApprover(this._state.dossier());
const applyDictionaryUpdatesToAllDossiersByDefault = dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault;
@ -560,6 +560,7 @@ export class AnnotationActionsService {
dossierId: this._state.dossierId,
applyToAllDossiers: isApprover ? applyDictionaryUpdatesToAllDossiersByDefault : false,
isApprover,
action,
};
}

View File

@ -75,7 +75,7 @@ export class PdfAnnotationActionsService {
if (permissions.canAcceptRecommendation && annotationChangesAllowed) {
const acceptRecommendationButton = this.#getButton('check', _('annotation-actions.accept-recommendation.label'), () =>
this.#annotationActionsService.convertRecommendationToAnnotation(annotations),
this.#annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept'),
);
availableActions.push(acceptRecommendationButton);
}

View File

@ -97,6 +97,7 @@ export interface RedactTextResult {
export type RedactRecommendationData = EditRedactionData & {
applyToAllDossiers: boolean;
action?: 'resize' | 'accept';
};
export interface RedactRecommendationResult {

View File

@ -3,6 +3,8 @@ import { CommonModule } from '@angular/common';
import { MatIconModule, MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { getConfig } from '@common-ui/services';
import { LANDING_PAGE_THEMES } from '@common-ui/utils/constants';
import { TenantsService } from '@common-ui/tenants';
@NgModule({
imports: [CommonModule, MatIconModule],
@ -15,6 +17,7 @@ export class IconsModule {
constructor(
private readonly _iconRegistry: MatIconRegistry,
private readonly _sanitizer: DomSanitizer,
private readonly _tenantsService: TenantsService,
) {
const icons = [
'ai',
@ -85,6 +88,9 @@ export class IconsModule {
'user',
'warning',
'pdftron-action-add-redaction',
'knecon-logo',
'redaction-logo',
'documine-logo',
];
for (const icon of icons) {
@ -95,12 +101,12 @@ export class IconsModule {
);
}
const logo = this.config.IS_DOCUMINE ? 'documine-logo' : 'logo';
const logo =
this.config.LANDING_PAGE_THEME === LANDING_PAGE_THEMES.MIXED ? 'knecon' : this.config.IS_DOCUMINE ? 'documine' : 'redaction';
_iconRegistry.addSvgIconInNamespace(
'iqser',
'logo',
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}.svg`),
_sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/general/${logo}-logo.svg`),
);
}
}

View File

@ -14,6 +14,7 @@ import { PdfViewer } from './pdf-viewer.service';
import Color = Core.Annotations.Color;
import DocumentViewer = Core.DocumentViewer;
import Quad = Core.Math.Quad;
import { isTargetInput } from '@utils/functions';
@Injectable()
export class REDDocumentViewer {
@ -71,12 +72,12 @@ export class REDDocumentViewer {
return fromEvent<KeyboardEvent>(this.#document, 'keyUp').pipe(
tap(stopAndPreventIfNotAllowed),
filter($event => {
if (($event.target as HTMLElement)?.tagName?.toLowerCase() === 'input') {
if (isTargetInput($event)) {
if ($event.key === 'Escape') {
return true;
}
}
return ($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input';
return isTargetInput($event);
}),
filter($event => $event.key.startsWith('Arrow') || ['f', 'h', 'H', 'Escape', 'Shift'].includes($event.key)),
tap<KeyboardEvent>(stopAndPrevent),

View File

@ -65,6 +65,21 @@ export class PdfViewer {
};
selectedText = '';
readonly escKeyHandler = {
keydown: (e: KeyboardEvent) => {
e.preventDefault();
this.#clickSelectToolButton();
},
keyup: (e: KeyboardEvent) => {
e.preventDefault();
if (this.#isElementActive('searchPanel') && !this._annotationManager.resizingAnnotationId) {
this.#focusViewer();
this.deactivateSearch();
}
this.#clickSelectToolButton();
},
};
constructor(
private readonly _logger: NGXLogger,
private readonly _errorService: ErrorService,
@ -115,6 +130,11 @@ export class PdfViewer {
return page$.pipe(map(page => this.#adjustPage(page)));
}
get #searchInput() {
const iframeWindow = this.#instance.UI.iframeWindow;
return iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
}
activateSearch() {
this.#instance.UI.searchTextFull('', this.searchOptions);
}
@ -165,7 +185,6 @@ export class PdfViewer {
this.#disableHotkeys();
this.#getSelectedText();
this.#listenForCommandF();
this.#listenForEsc();
this.#listenForShift();
this.#clearSearchResultsWhenVisibilityChanged();
});
@ -273,32 +292,15 @@ export class PdfViewer {
});
}
#listenForEsc() {
this.#instance.UI.hotkeys.on('esc', {
keydown: (e: KeyboardEvent) => {
e.preventDefault();
this.#clickSelectToolButton();
},
keyup: (e: KeyboardEvent) => {
e.preventDefault();
if (this.#isElementActive('searchPanel') && !this._annotationManager.resizingAnnotationId) {
this.#focusViewer();
this.deactivateSearch();
}
this.#clickSelectToolButton();
},
});
}
#listenForShift() {
this.#instance.UI.iframeWindow.addEventListener('keydown', e => {
e.preventDefault();
if (e.target === this.#searchInput) return;
if (e.key === 'Shift') {
this.#setSelectionMode(SelectionModes.RECTANGULAR);
}
});
this.#instance.UI.iframeWindow.addEventListener('keyup', e => {
e.preventDefault();
if (e.target === this.#searchInput) return;
if (e.key === 'Shift') {
this.#setSelectionMode(SelectionModes.STRUCTURAL);
}
@ -395,13 +397,11 @@ export class PdfViewer {
if (this.#isElementActive('textPopup')) {
this.#instance.UI.closeElements(['textPopup']);
}
const iframeWindow = this.#instance.UI.iframeWindow;
const input = iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement;
if (input) {
input.focus();
if (this.#searchInput) {
this.#searchInput.focus();
}
if (input?.value?.length > 0) {
input.select();
if (this.#searchInput?.value?.length > 0) {
this.#searchInput.select();
}
}

View File

@ -13,7 +13,7 @@ import {
} from '@iqser/common-ui';
import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { IqserTooltipPositions } from '@iqser/common-ui/lib/utils';
import { Action, ActionTypes, ApproveResponse, Dossier, File, ProcessingFileStatuses, User } from '@red/domain';
import { Action, ActionTypes, ApproveResponse, Dossier, File, User } from '@red/domain';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { FileManagementService } from '@services/files/file-management.service';
@ -69,15 +69,21 @@ export class FileActionsComponent {
this.file().isUnderApproval ? _('dossier-overview.assign-approver') : _('dossier-overview.assign-reviewer'),
);
readonly #showSetToNew = computed(
() => this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(),
() =>
this._permissionsService.canSetToNew(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow() && !this.file().isError,
);
readonly #showUndoApproval = computed(
() => this._permissionsService.canUndoApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(),
() =>
this._permissionsService.canUndoApproval(this.file(), this.dossier()) &&
!this.isDossierOverviewWorkflow() &&
!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()));
readonly #showImportRedactions = computed(
() => this._permissionsService.canImportRedactions(this.file(), this.dossier()) && !this.file().isError,
);
readonly #showAssign = computed(
() =>
(this._permissionsService.canAssignUser(this.file(), this.dossier()) ||
@ -87,15 +93,26 @@ export class FileActionsComponent {
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()));
readonly #canEnableAutoAnalysis = computed(
() => this._permissionsService.canEnableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
);
readonly #showUnderReview = computed(
() => this._permissionsService.canSetUnderReview(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(),
() =>
this._permissionsService.canSetUnderReview(this.file(), this.dossier()) &&
!this.isDossierOverviewWorkflow() &&
!this.file().isError,
);
readonly #showUnderApproval = computed(
() => this._permissionsService.canSetUnderApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(),
() =>
this._permissionsService.canSetUnderApproval(this.file(), this.dossier()) &&
!this.isDossierOverviewWorkflow() &&
!this.file().isError,
);
readonly #showApprove = computed(
() => this._permissionsService.isReadyForApproval(this.file(), this.dossier()) && !this.isDossierOverviewWorkflow(),
() =>
this._permissionsService.isReadyForApproval(this.file(), this.dossier()) &&
!this.isDossierOverviewWorkflow() &&
!this.file().isError,
);
readonly #canToggleAnalysis = computed(() => this._permissionsService.canToggleAnalysis(this.file(), this.dossier()));
readonly #toggleTooltip? = computed(() => {
@ -130,7 +147,7 @@ export class FileActionsComponent {
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly #canDisableAutoAnalysis = computed(
() => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()),
() => !this.#isDocumine && this._permissionsService.canDisableAutoAnalysis([this.file()], this.dossier()) && !this.file().isError,
);
constructor(

View File

@ -10,6 +10,7 @@
[buttonId]="buttonId()"
icon="iqser:download"
dropdownButton
iqserStopPropagation
></iqser-circle-button>
} @else {
<iqser-circle-button

View File

@ -1,6 +1,8 @@
<section class="dialog">
<form [formGroup]="form">
<div [translate]="'add-dossier-dialog.header-new'" class="dialog-header heading-l"></div>
<div class="dialog-header heading-l">
<span [translate]="'add-dossier-dialog.header-new'" [attr.help-mode-key]="'new_dossier_DIALOG'"></span>
</div>
<div *ngIf="config.IS_DOCUMINE && !availableReportTypes.length" class="inline-dialog-toast toast-warning">
<div [translate]="'add-dossier-dialog.no-report-types-warning'"></div>

View File

@ -496,7 +496,7 @@ export class PermissionsService {
}
#canAssignReviewer(file: File, dossier: Dossier) {
const fileStatesForReviewer = file.isNew || file.isUnderReview || file.isUnderApproval;
const fileStatesForReviewer = file.isNew || file.isUnderReview || file.isUnderApproval || file.isApproved;
return fileStatesForReviewer && this.isDossierMember(dossier);
}

View File

@ -57,7 +57,7 @@ export class TranslateChartService {
translateRoles(config: DonutChartConfig[]): DonutChartConfig[] {
return config.map(val => ({
...val,
label: this._translateService.instant(rolesTranslations[val.label], { length: val.value }),
label: this._translateService.instant(rolesTranslations[val.label], { count: val.value }),
}));
}
}

View File

@ -1,5 +1,5 @@
import { ITrackable } from '@iqser/common-ui';
import type { List } from '@iqser/common-ui/lib/utils';
import type { IqserEventTarget, List } from '@iqser/common-ui/lib/utils';
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Dayjs } from 'dayjs';
import { AbstractControl } from '@angular/forms';
@ -149,3 +149,11 @@ export function urlFileId() {
export function formControlToSignal<T>(control: AbstractControl<T>) {
return toSignal(control.valueChanges, { initialValue: control.value });
}
export function isTargetInput(event: Event) {
return (event?.target as IqserEventTarget)?.localName === 'input';
}
export function isTargetTextArea(event: Event) {
return (event?.target as IqserEventTarget)?.localName === 'textarea';
}

View File

@ -60,7 +60,9 @@ export function mainGuard(): AsyncGuard {
}
loadingService.stop();
tenantsService.storeTenant();
await tenantsService.storeTenant();
configService.updateIsDocumine(tenantsService.activeTenant);
logger.info('[ROUTES] Main guard finished!');

View File

@ -1,9 +1,8 @@
{
"ADMIN_CONTACT_NAME": null,
"ADMIN_CONTACT_URL": null,
"API_URL": "https://dan1.iqser.cloud",
"APP_NAME": "RedactManager",
"IS_DOCUMINE": false,
"API_URL": "https://dan2.iqser.cloud",
"APP_NAME": "Knecon Cloud",
"RULE_EDITOR_DEV_ONLY": false,
"AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40",
@ -13,15 +12,15 @@
"MAX_RETRIES_ON_SERVER_ERROR": 3,
"OAUTH_CLIENT_ID": "redaction",
"OAUTH_IDP_HINT": null,
"OAUTH_URL": "https://dan1.iqser.cloud/auth",
"OAUTH_URL": "https://dan2.iqser.cloud/auth",
"RECENT_PERIOD_IN_HOURS": 24,
"SELECTION_MODE": "structural",
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
"ANNOTATIONS_THRESHOLD": 1000,
"THEME": "redact",
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/redact/",
"AVAILABLE_NOTIFICATIONS_DAYS": 30,
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES": 60,
"NOTIFICATIONS_THRESHOLD": 1000,
"WATERMARK_PREVIEW_PAPER_FORMAT": "a4"
"WATERMARK_PREVIEW_PAPER_FORMAT": "a4",
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/",
"LANDING_PAGE_THEME": "mixed"
}

View File

@ -36,6 +36,11 @@
"documentKey": "new_dossier",
"overlappingElements": ["USER_MENU"]
},
{
"elementKey": "new_dossier_DIALOG",
"documentKey": "new_dossier",
"overlappingElements": ["USER_MENU"]
},
{
"elementKey": "open_notifications",
"documentKey": "open_notifications"
@ -114,48 +119,84 @@
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "redaction_resize_DIALOG",
"documentKey": "redaction_resize"
},
{
"elementKey": "redaction_edit",
"documentKey": "redaction_edit",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "redaction_edit_DIALOG",
"documentKey": "redaction_edit"
},
{
"elementKey": "add_redaction_DIALOG",
"documentKey": "add_redaction"
},
{
"elementKey": "redaction_remove",
"documentKey": "redaction_remove",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "redaction_remove_DIALOG",
"documentKey": "redaction_remove"
},
{
"elementKey": "recommendation_resize",
"documentKey": "recommendation_resize",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "recommendation_resize_DIALOG",
"documentKey": "recommendation_resize"
},
{
"elementKey": "recommendation_accept",
"documentKey": "recommendation_accept",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "recommendation_accept_DIALOG",
"documentKey": "recommendation_accept"
},
{
"elementKey": "recommendation_remove",
"documentKey": "recommendation_remove",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "recommendation_remove_DIALOG",
"documentKey": "recommendation_remove"
},
{
"elementKey": "skipped_edit",
"documentKey": "skipped_edit",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "skipped_edit_DIALOG",
"documentKey": "skipped_edit"
},
{
"elementKey": "skipped_force",
"documentKey": "skipped_force",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "skipped_force_DIALOG",
"documentKey": "skipped_force"
},
{
"elementKey": "skipped_remove",
"documentKey": "skipped_remove",
@ -174,18 +215,30 @@
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "hint_resize_DIALOG",
"documentKey": "hint_resize"
},
{
"elementKey": "hint_edit",
"documentKey": "hint_edit",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "hint_edit_DIALOG",
"documentKey": "hint_edit"
},
{
"elementKey": "hint_remove",
"documentKey": "hint_remove",
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "hint_remove_DIALOG",
"documentKey": "hint_remove"
},
{
"elementKey": "hint_hide",
"documentKey": "hint_hide",
@ -198,6 +251,10 @@
"scrollableParentView": "ANNOTATIONS_LIST",
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
},
{
"elementKey": "hint_redact_DIALOG",
"documentKey": "hint_redact"
},
{
"elementKey": "workload_in_editor",
"documentKey": "workload_in_editor",

View File

@ -1,7 +1,7 @@
{
"accept-recommendation-dialog": {
"header": {
"add-to-dictionary": "Zum Wörterbuch hinzufügen",
"add-to-dictionary": "Fügen Sie die Schwärzung auf mehreren Seiten hinzu.",
"request-add-to-dictionary": "Wörterbucheintrag vorschlagen"
}
},
@ -530,7 +530,7 @@
},
"edit-title": "Edit {displayName} definition",
"form": {
"autogenerated-label": "Autogenerated based on the initial display name",
"autogenerated-label": "Wurde ausgehend vom initialen Anzeigenamen automatisch generiert",
"description": "Beschreibung",
"description-placeholder": "Beschreibung",
"display-name": "Display Name",
@ -641,7 +641,7 @@
},
"confirmation-dialog": {
"approve-file": {
"confirmationText": "Trotzdem genehmigen",
"confirmationText": "Dennoch freigeben",
"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?",
"title": "Warnung!",
@ -1618,7 +1618,7 @@
},
"file-status": {
"analyse": "Analyse läuft",
"approved": "Genehmigt",
"approved": "Freigegeben",
"error": "Reanalyse erforderlich",
"figure-detection-analyzing": "",
"full-processing": "Verarbeitung läuft",
@ -2385,7 +2385,7 @@
"red-manager": "{count, plural, one{Manager} other{Manager}}",
"red-user": "Benutzer",
"red-user-admin": "{count, plural, one{Benutzeradmin} other{Benutzeradmins}}",
"regular": "{count, plural, one{{regulärer Benutzer}} other{reguläre Benutzer}}"
"regular": "{count, plural, one{regulärer Benutzer} other{reguläre Benutzer}}"
},
"search": {
"active-dossiers": "Dokumente in aktiven Dossiers",

View File

@ -0,0 +1 @@
<svg id="ab727bb5-bfa8-48f3-98cb-a1a7576570a7" data-name="Ebene 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 280"><defs><style>.f0575b6c-6418-41d5-a2bf-731ad5aa403e{fill:#d2302c;}</style></defs><path class="f0575b6c-6418-41d5-a2bf-731ad5aa403e" d="M0,0V280H280V0ZM220.11072,209.75043a.24953.24953,0,0,1-.24964.24957l-79.56158-.011a.74925.74925,0,0,1-.52991-.21954L80,150v59.75043A.2496.2496,0,0,1,79.75043,210H60.24957A.2496.2496,0,0,1,60,209.75043V70.24957A.2496.2496,0,0,1,60.24957,70H79.75043A.2496.2496,0,0,1,80,70.24957V130l59.78046-59.78046a.7496.7496,0,0,1,.53-.21954h79.43994A.2496.2496,0,0,1,220,70.24957v19.5008A.24961.24961,0,0,1,219.75043,90l-71.35089-.004a.24958.24958,0,0,0-.17658.07318L98.25555,140.10248,148.128,189.92694a.24953.24953,0,0,0,.17639.07306h71.55677a.2496.2496,0,0,1,.24957.24957ZM220,130.25v19.5a.25.25,0,0,1-.25.25h-79.5a.25.25,0,0,1-.25-.25v-19.5a.25.25,0,0,1,.25-.25h79.5A.25.25,0,0,1,220,130.25Z"/></svg>

After

Width:  |  Height:  |  Size: 948 B

View File

Before

Width:  |  Height:  |  Size: 942 B

After

Width:  |  Height:  |  Size: 942 B

View File

@ -5,6 +5,7 @@ import { bool } from '@common-ui/utils';
import { environment } from '@environments/environment';
import { AppConfig } from '@red/domain';
import { appModuleFactory } from './app/app.module';
import { LANDING_PAGE_THEMES, THEME_DIRECTORIES } from '@common-ui/utils/constants';
if (environment.production) {
enableProdMode();
@ -13,12 +14,15 @@ if (environment.production) {
// https://github.com/angular/angular/issues/11195#issuecomment-248020928
async function bootstrap(appConfig: AppConfig, version: { FRONTEND_APP_VERSION: string }) {
const ruleEditorDevOnly = appConfig.RULE_EDITOR_DEV_ONLY as unknown as string | boolean;
const isDocumine = bool(appConfig.LANDING_PAGE_THEME !== LANDING_PAGE_THEMES.REDACT_MANAGER);
const config = {
...appConfig,
IS_DOCUMINE: bool(appConfig.IS_DOCUMINE),
IS_DOCUMINE: isDocumine,
THEME: !isDocumine ? THEME_DIRECTORIES.REDACT : THEME_DIRECTORIES.SCM,
RULE_EDITOR_DEV_ONLY:
typeof ruleEditorDevOnly === 'string' ? ruleEditorDevOnly.toLowerCase() !== 'false' : ruleEditorDevOnly !== false,
FRONTEND_APP_VERSION: version.FRONTEND_APP_VERSION,
APP_NAME: appConfig.LANDING_PAGE_THEME === LANDING_PAGE_THEMES.MIXED ? 'Knecon Cloud' : isDocumine ? 'DocuMine' : 'RedactManager',
} as AppConfig;
console.log('Started with local config: ', config);
const appModule = appModuleFactory(config);

View File

@ -4,13 +4,11 @@ ADMIN_CONTACT_NAME="${ADMIN_CONTACT_NAME:-}"
ADMIN_CONTACT_URL="${ADMIN_CONTACT_URL:-}"
API_URL="${API_URL:-}"
APP_NAME="${APP_NAME:-}"
IS_DOCUMINE="${IS_DOCUMINE:-false}"
RULE_EDITOR_DEV_ONLY="${RULE_EDITOR_DEV_ONLY:-true}"
AUTO_READ_TIME="${AUTO_READ_TIME:-1.5}"
BACKEND_APP_VERSION="${BACKEND_APP_VERSION:-4.7.0}"
EULA_URL="${EULA_URL:-}"
FRONTEND_APP_VERSION="${FRONTEND_APP_VERSION:-}"
MAX_FILE_SIZE_MB="${MAX_FILE_SIZE_MB:-50}"
MAX_RETRIES_ON_SERVER_ERROR="${MAX_RETRIES_ON_SERVER_ERROR:-3}"
OAUTH_CLIENT_ID="${OAUTH_CLIENT_ID:-gin-client}"
@ -23,16 +21,14 @@ ANNOTATIONS_THRESHOLD="${ANNOTATIONS_THRESHOLD:-1000}"
AVAILABLE_NOTIFICATIONS_DAYS="${AVAILABLE_NOTIFICATIONS_DAYS:-30}"
AVAILABLE_OLD_NOTIFICATIONS_MINUTES="${AVAILABLE_OLD_NOTIFICATIONS_MINUTES:-60}"
NOTIFICATIONS_THRESHOLD="${NOTIFICATIONS_THRESHOLD:-1000}"
BASE_TRANSLATIONS_DIRECTORY="${BASE_TRANSLATIONS_DIRECTORY:-/assets/i18n/redact/}"
THEME="${THEME:-theme-template}"
WATERMARK_PREVIEW_PAPER_FORMAT="${WATERMARK_PREVIEW_PAPER_FORMAT:-a4}"
LANDING_PAGE_THEME="${LANDING_PAGE_THEME:-redactmanager}"
echo '{
"ADMIN_CONTACT_NAME":"'"$ADMIN_CONTACT_NAME"'",
"ADMIN_CONTACT_URL":"'"$ADMIN_CONTACT_URL"'",
"API_URL":"'"$API_URL"'",
"APP_NAME":"'"$APP_NAME"'",
"IS_DOCUMINE":"'"$IS_DOCUMINE"'",
"RULE_EDITOR_DEV_ONLY":"'"$RULE_EDITOR_DEV_ONLY"'",
"AUTO_READ_TIME":"'"$AUTO_READ_TIME"'",
"BACKEND_APP_VERSION":"'"$BACKEND_APP_VERSION"'",
@ -46,13 +42,12 @@ echo '{
"RECENT_PERIOD_IN_HOURS":"'"$RECENT_PERIOD_IN_HOURS"'",
"SELECTION_MODE":"'"$SELECTION_MODE"'",
"MANUAL_BASE_URL":"'"$MANUAL_BASE_URL"'",
"BASE_TRANSLATIONS_DIRECTORY":"'"$BASE_TRANSLATIONS_DIRECTORY"'",
"THEME":"'"$THEME"'",
"ANNOTATIONS_THRESHOLD":"'"$ANNOTATIONS_THRESHOLD"'",
"AVAILABLE_NOTIFICATIONS_DAYS":"'"$AVAILABLE_NOTIFICATIONS_DAYS"'",
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES":"'"$AVAILABLE_OLD_NOTIFICATIONS_MINUTES"'",
"NOTIFICATIONS_THRESHOLD":"'"$NOTIFICATIONS_THRESHOLD"'",
"WATERMARK_PREVIEW_PAPER_FORMAT":"'"$WATERMARK_PREVIEW_PAPER_FORMAT"'"
"LANDING_PAGE_THEME":"'"$LANDING_PAGE_THEME"'"
}' >/usr/share/nginx/html/ui/assets/config/config.json
echo 'App config: '

@ -1 +1 @@
Subproject commit cbdfcf4d8fb3a63417b61604496d8fa81bdea671
Subproject commit b929f1d13687ebd0a4887c620851ae168fc21935