diff --git a/apps/red-ui/src/app/app.component.ts b/apps/red-ui/src/app/app.component.ts index 4e78439d4..f4667e187 100644 --- a/apps/red-ui/src/app/app.component.ts +++ b/apps/red-ui/src/app/app.component.ts @@ -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().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(); 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() { diff --git a/apps/red-ui/src/app/app.module.ts b/apps/red-ui/src/app/app.module.ts index 82d09377f..5691a4dc3 100644 --- a/apps/red-ui/src/app/app.module.ts +++ b/apps/red-ui/src/app/app.module.ts @@ -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, { diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html index 0adcdcfab..7d82816c3 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.html @@ -32,7 +32,7 @@ [formControlName]="role" color="primary" > - {{ translations[role] | translate }} + {{ translations[role] | translate: { count: 1 } }} diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts index f1c04fec6..9111e481c 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-user-dialog/user-details/user-details.component.ts @@ -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'; diff --git a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts index 942cfbfbf..d39a25316 100644 --- a/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/user-listing/user-listing-screen.component.ts @@ -126,7 +126,7 @@ export class UserListingScreenComponent extends ListingComponent 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']); } diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts index a8faf210e..0de86597d 100644 --- a/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier-overview/components/bulk-actions/dossier-overview-bulk-actions.component.ts @@ -26,6 +26,7 @@ export class DossierOverviewBulkActionsComponent { readonly maxWidth = input(); 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'), ); diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts index 2ec1353b6..82fcc69aa 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.ts @@ -126,7 +126,7 @@ export class AnnotationActionsComponent { async acceptRecommendation(): Promise { const annotations = untracked(this.annotations); - await this.annotationActionsService.convertRecommendationToAnnotation(annotations); + await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept'); } hideAnnotation() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.ts b/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.ts index a092c15b3..b97d25475 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.ts @@ -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', diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts b/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts index 1b5deaff9..5bd7b034c 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/annotations-list/annotations-list.component.ts @@ -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; } diff --git a/apps/red-ui/src/app/modules/file-preview/components/documine-export/documine-export.component.ts b/apps/red-ui/src/app/modules/file-preview/components/documine-export/documine-export.component.ts index 071f8df4a..d56162d22 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/documine-export/documine-export.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/documine-export/documine-export.component.ts @@ -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(); diff --git a/apps/red-ui/src/app/modules/file-preview/components/editable-structured-component-value/editable-structured-component-value.component.scss b/apps/red-ui/src/app/modules/file-preview/components/editable-structured-component-value/editable-structured-component-value.component.scss index 8e4b4194b..deeacae0f 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/editable-structured-component-value/editable-structured-component-value.component.scss +++ b/apps/red-ui/src/app/modules/file-preview/components/editable-structured-component-value/editable-structured-component-value.component.scss @@ -20,6 +20,12 @@ display: flex; flex-direction: column; gap: 10px; + + span { + word-wrap: break-word; + word-break: break-word; + text-overflow: ellipsis; + } } .actions { diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts index d1b164b70..bb4076329 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-header/file-header.component.ts @@ -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() { diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts index e7e0199c0..850108023 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts @@ -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(); } } diff --git a/apps/red-ui/src/app/modules/file-preview/components/readonly-banner/readonly-banner.component.html b/apps/red-ui/src/app/modules/file-preview/components/readonly-banner/readonly-banner.component.html index 1a0a8f423..f37c4646c 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/readonly-banner/readonly-banner.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/readonly-banner/readonly-banner.component.html @@ -1,24 +1,35 @@ -