Merge branch 'master' into VM/RED-3982

This commit is contained in:
Valentin Mihai 2022-05-17 00:37:51 +03:00
commit c7217d7ae4
30 changed files with 473 additions and 403 deletions

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate } from '@angular/router'; import { CanActivate } from '@angular/router';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service'; import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
@ -7,7 +7,7 @@ import { DashboardStatsService } from '@services/dossier-templates/dashboard-sta
export class DashboardGuard implements CanActivate { export class DashboardGuard implements CanActivate {
constructor(private readonly _dashboardStatsService: DashboardStatsService) {} constructor(private readonly _dashboardStatsService: DashboardStatsService) {}
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> { async canActivate(): Promise<boolean> {
await firstValueFrom(this._dashboardStatsService.loadAll()); await firstValueFrom(this._dashboardStatsService.loadAll());
return true; return true;
} }

View File

@ -2,20 +2,33 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DOSSIER_TEMPLATE_ID } from '@red/domain'; import { DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service'; import { DashboardStatsService } from '@services/dossier-templates/dashboard-stats.service';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class DossierTemplateExistsGuard implements CanActivate { export class DossierTemplateExistsGuard implements CanActivate {
constructor(private readonly _dashboardStatsService: DashboardStatsService, private readonly _router: Router) {} constructor(
private readonly _dashboardStatsService: DashboardStatsService,
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _router: Router,
) {}
async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> { async canActivate(route: ActivatedRouteSnapshot): Promise<boolean> {
const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID); const dossierTemplateId: string = route.paramMap.get(DOSSIER_TEMPLATE_ID);
const dossiersListView = !route.pathFromRoot.find(r => r.routeConfig?.path === 'admin'); const dossiersListView = !route.pathFromRoot.find(r => r.routeConfig?.path === 'admin');
if (dossiersListView) {
const dossierTemplateStats = this._dashboardStatsService.find(dossierTemplateId); const dossierTemplateStats = this._dashboardStatsService.find(dossierTemplateId);
if (!dossierTemplateStats || (dossiersListView && dossierTemplateStats.isEmpty)) { if (!dossierTemplateStats || (dossiersListView && dossierTemplateStats.isEmpty)) {
const routerPath = dossiersListView ? [''] : ['main', 'admin', 'dossier-templates']; await this._router.navigate(['']);
await this._router.navigate(routerPath);
return false; return false;
} }
} else {
const dossierTemplate = this._dossierTemplatesService.find(dossierTemplateId);
if (!dossierTemplate) {
await this._router.navigate(['main', 'admin', 'dossier-templates']);
return false;
}
}
return true; return true;
} }

View File

@ -10,7 +10,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@services/entity-services/dictionary.service'; import { DictionaryService } from '@services/entity-services/dictionary.service';
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui'; import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
const REDACTION_FIELDS = ['defaultReason', 'caseSensitive', 'addToDictionaryAction']; const REDACTION_FIELDS = ['defaultReason', 'addToDictionaryAction'];
@Component({ @Component({
selector: 'redaction-add-edit-entity [entity] [dossierTemplateId]', selector: 'redaction-add-edit-entity [entity] [dossierTemplateId]',
@ -38,10 +38,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
super(); super();
} }
get #caseSensitiveControl() {
return { value: this.entity ? !this.entity.caseInsensitive : false, disabled: this.#isSystemManaged };
}
get #isSystemManaged(): boolean { get #isSystemManaged(): boolean {
return !!this.entity?.systemManaged; return !!this.entity?.systemManaged;
} }
@ -91,12 +87,12 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
recommendationHexColor: [this.entity?.recommendationHexColor, [Validators.required, Validators.minLength(7)]], recommendationHexColor: [this.entity?.recommendationHexColor, [Validators.required, Validators.minLength(7)]],
hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }], hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }],
hasDictionary: [{ value: !!this.entity?.hasDictionary, disabled: this.#isSystemManaged }], hasDictionary: [{ value: !!this.entity?.hasDictionary, disabled: this.#isSystemManaged }],
caseSensitive: [{ value: this.entity ? !this.entity.caseInsensitive : false, disabled: this.#isSystemManaged }],
}; };
if (!this.entity?.hint) { if (!this.entity?.hint) {
Object.assign(controlsConfig, { Object.assign(controlsConfig, {
defaultReason: [{ value: null, disabled: true }], defaultReason: [{ value: null, disabled: true }],
caseSensitive: [this.#caseSensitiveControl],
}); });
if (this.entity?.hasDictionary) { if (this.entity?.hasDictionary) {
@ -116,7 +112,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
REDACTION_FIELDS.forEach(field => form.removeControl(field)); REDACTION_FIELDS.forEach(field => form.removeControl(field));
} else { } else {
form.addControl('defaultReason', new FormControl({ value: null, disabled: true })); form.addControl('defaultReason', new FormControl({ value: null, disabled: true }));
form.addControl('caseSensitive', new FormControl(this.#caseSensitiveControl));
if (form.get('hasDictionary').value) { if (form.get('hasDictionary').value) {
form.addControl('addToDictionaryAction', new FormControl(this.#addToDictionaryActionControl)); form.addControl('addToDictionaryAction', new FormControl(this.#addToDictionaryActionControl));
@ -167,7 +162,6 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
private _formToObject(): IDictionary { private _formToObject(): IDictionary {
// Fields that aren't set for hints, need additional check // Fields that aren't set for hints, need additional check
const caseInsensitive = this.form.get('caseSensitive') ? !this.form.get('caseSensitive').value : null;
const addToDictionaryAction = !!this.form.get('addToDictionaryAction')?.value; const addToDictionaryAction = !!this.form.get('addToDictionaryAction')?.value;
const hasDictionary = !!this.form.get('hasDictionary')?.value; const hasDictionary = !!this.form.get('hasDictionary')?.value;
@ -180,7 +174,7 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
hint: this.form.get('hint').value, hint: this.form.get('hint').value,
rank: this.form.get('rank').value, rank: this.form.get('rank').value,
dossierTemplateId: this.dossierTemplateId, dossierTemplateId: this.dossierTemplateId,
caseInsensitive, caseInsensitive: !this.form.get('caseSensitive').value,
addToDictionaryAction, addToDictionaryAction,
hasDictionary, hasDictionary,
}; };

View File

@ -107,7 +107,7 @@ export class ConfigService {
label: myDossiersLabel, label: myDossiersLabel,
checker: this._myDossiersChecker, checker: this._myDossiersChecker,
disabled: entities.filter(this._myDossiersChecker).length === 0, disabled: entities.filter(this._myDossiersChecker).length === 0,
helpModeKey: 'dossiers_quickfilter_my_dossiers', helpModeKey: 'filter_dossier_list',
}, },
].map(filter => new NestedFilter(filter)); ].map(filter => new NestedFilter(filter));
} }

View File

@ -5,7 +5,6 @@ import { HttpClientModule } from '@angular/common/http';
import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular'; import { KeycloakAngularModule, KeycloakOptions, KeycloakService } from 'keycloak-angular';
import { ConfigService } from '@services/config.service'; import { ConfigService } from '@services/config.service';
import { BASE_HREF } from '../../tokens'; import { BASE_HREF } from '../../tokens';
import { firstValueFrom } from 'rxjs';
function getKeycloakOptions(configService: ConfigService, baseUrl: string) { function getKeycloakOptions(configService: ConfigService, baseUrl: string) {
let url: string = configService.values.OAUTH_URL; let url: string = configService.values.OAUTH_URL;
@ -37,11 +36,9 @@ function configureAutomaticRedirectToLoginScreen(keyCloakService: KeycloakServic
export function keycloakInitializer(keycloakService: KeycloakService, configService: ConfigService, baseUrl: string): () => Promise<void> { export function keycloakInitializer(keycloakService: KeycloakService, configService: ConfigService, baseUrl: string): () => Promise<void> {
return () => return () =>
firstValueFrom(configService.loadAppConfig()).then(() =>
keycloakService keycloakService
.init(getKeycloakOptions(configService, baseUrl)) .init(getKeycloakOptions(configService, baseUrl))
.then(() => configureAutomaticRedirectToLoginScreen(keycloakService)), .then(() => configureAutomaticRedirectToLoginScreen(keycloakService));
);
} }
@NgModule({ @NgModule({

View File

@ -1,5 +1,5 @@
import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Dossier, IDictionary } from '@red/domain'; import { Dictionary, Dossier, IDictionary } from '@red/domain';
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface'; import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component'; import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
@ -21,7 +21,7 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
form: FormGroup; form: FormGroup;
canEdit = false; canEdit = false;
canEditDisplayName = false; canEditDisplayName = false;
dossierDictionary: IDictionary; dossierDictionary: Dictionary;
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
@ViewChild(DictionaryManagerComponent, { static: false }) private readonly _dictionaryManager: DictionaryManagerComponent; @ViewChild(DictionaryManagerComponent, { static: false }) private readonly _dictionaryManager: DictionaryManagerComponent;
@ -130,8 +130,6 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
private async _updateDossierDictionary() { private async _updateDossierDictionary() {
const { dossierId, dossierTemplateId } = this.dossier; const { dossierId, dossierTemplateId } = this.dossier;
this.dossierDictionary = await firstValueFrom( this.dossierDictionary = await firstValueFrom(this._dictionaryService.getDossierDictionary(dossierTemplateId, dossierId));
this._dictionaryService.getForType(dossierTemplateId, 'dossier_redaction', dossierId),
);
} }
} }

View File

@ -74,7 +74,10 @@ export class AnnotationProcessingService {
if (!parentFilter) { if (!parentFilter) {
parentFilter = this._createParentFilter(a.superType, filterMap, filters); parentFilter = this._createParentFilter(a.superType, filterMap, filters);
} }
const dictionary = this._dictionariesMapService.getDictionary(a.type, this._state.dossierTemplateId); const dictionary =
a.type === 'dossier_redaction'
? this._state.dossierDictionary
: this._dictionariesMapService.getDictionary(a.type, this._state.dossierTemplateId);
const childFilter: IFilter = { const childFilter: IFilter = {
id: a.filterKey, id: a.filterKey,
label: dictionary.label, label: dictionary.label,

View File

@ -203,7 +203,7 @@ export class ConfigService {
label: myDossiersLabel, label: myDossiersLabel,
checker: this._myDossiersChecker, checker: this._myDossiersChecker,
disabled: entities.filter(this._myDossiersChecker).length === 0, disabled: entities.filter(this._myDossiersChecker).length === 0,
helpModeKey: 'dossiers_quickfilter_my_dossiers', helpModeKey: 'filter_dossier_list',
}, },
{ {
id: 'to-approve', id: 'to-approve',

View File

@ -1,11 +1,13 @@
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { Dictionary, DOSSIER_ID } from '@red/domain'; import { Dictionary, DOSSIER_ID } from '@red/domain';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { DossiersService } from '@services/dossiers/dossiers.service'; import { DossiersService } from '@services/dossiers/dossiers.service';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { DictionaryService } from '@services/entity-services/dictionary.service';
@Component({ @Component({
selector: 'redaction-annotation-card', selector: 'redaction-annotation-card',
@ -17,6 +19,7 @@ export class AnnotationCardComponent implements OnChanges {
@Input() annotation: AnnotationWrapper; @Input() annotation: AnnotationWrapper;
@Input() isSelected = false; @Input() isSelected = false;
readonly dictionary$ = new BehaviorSubject<Dictionary>(undefined); readonly dictionary$ = new BehaviorSubject<Dictionary>(undefined);
readonly #dossierId: string;
readonly #dossierTemplateId: string; readonly #dossierTemplateId: string;
constructor( constructor(
@ -24,15 +27,20 @@ export class AnnotationCardComponent implements OnChanges {
dossiersService: DossiersService, dossiersService: DossiersService,
readonly multiSelectService: MultiSelectService, readonly multiSelectService: MultiSelectService,
private readonly _dictionariesMapService: DictionariesMapService, private readonly _dictionariesMapService: DictionariesMapService,
private readonly _dictionaryService: DictionaryService,
private readonly _state: FilePreviewStateService,
) { ) {
const dossierId: string = route.snapshot.paramMap.get(DOSSIER_ID); this.#dossierId = route.snapshot.paramMap.get(DOSSIER_ID);
this.#dossierTemplateId = dossiersService.find(dossierId).dossierTemplateId; this.#dossierTemplateId = dossiersService.find(this.#dossierId).dossierTemplateId;
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes.annotation) { if (changes.annotation) {
if (this.annotation.type !== 'manual' && !this.annotation.isHighlight) { if (this.annotation.type !== 'manual' && !this.annotation.isHighlight) {
const dictionary = this._dictionariesMapService.getDictionary(this.annotation.type, this.#dossierTemplateId); const dictionary =
this.annotation.type === 'dossier_redaction'
? this._state.dossierDictionary
: this._dictionariesMapService.getDictionary(this.annotation.type, this.#dossierTemplateId);
this.dictionary$.next(dictionary); this.dictionary$.next(dictionary);
} }
} }

View File

@ -28,7 +28,6 @@ import { PdfViewer } from '../../services/pdf-viewer.service';
export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges { export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges {
@Input() annotations: AnnotationWrapper[]; @Input() annotations: AnnotationWrapper[];
@Input() annotationActionsTemplate: TemplateRef<unknown>; @Input() annotationActionsTemplate: TemplateRef<unknown>;
@Input() activeViewerPage: number;
@Output() readonly pagesPanelActive = new EventEmitter<boolean>(); @Output() readonly pagesPanelActive = new EventEmitter<boolean>();

View File

@ -48,7 +48,7 @@
<div *ngIf="multiSelectService.active$ | async" class="multi-select"> <div *ngIf="multiSelectService.active$ | async" class="multi-select">
<div class="selected-wrapper"> <div class="selected-wrapper">
<iqser-round-checkbox <iqser-round-checkbox
(click)="pdf.selectAnnotations()" (click)="pdf.deselectAllAnnotations()"
[indeterminate]="listingService.areSomeSelected$ | async" [indeterminate]="listingService.areSomeSelected$ | async"
type="with-bg" type="with-bg"
></iqser-round-checkbox> ></iqser-round-checkbox>
@ -213,7 +213,6 @@
<redaction-annotations-list <redaction-annotations-list
(pagesPanelActive)="pagesPanelActive = $event" (pagesPanelActive)="pagesPanelActive = $event"
[activeViewerPage]="activeViewerPage"
[annotationActionsTemplate]="annotationActionsTemplate" [annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)" [annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
></redaction-annotations-list> ></redaction-annotations-list>

View File

@ -134,7 +134,7 @@ export class FileWorkloadComponent {
return this.multiSelectService.inactive$.pipe( return this.multiSelectService.inactive$.pipe(
tap(value => { tap(value => {
if (value) { if (value) {
this.pdf.selectAnnotations(); this.pdf.deselectAllAnnotations();
} }
}), }),
shareDistinctLast(), shareDistinctLast(),

View File

@ -31,7 +31,7 @@ import { download, handleFilterDelta } from '../../utils';
import { FileWorkloadComponent } from './components/file-workload/file-workload.component'; import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
import { FilesService } from '@services/entity-services/files.service'; import { FilesService } from '@services/entity-services/files.service';
import { FileManagementService } from '@services/entity-services/file-management.service'; import { FileManagementService } from '@services/entity-services/file-management.service';
import { catchError, debounceTime, map, startWith, switchMap, tap } from 'rxjs/operators'; import { catchError, map, startWith, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesMapService } from '@services/entity-services/files-map.service';
import { ExcludedPagesService } from './services/excluded-pages.service'; import { ExcludedPagesService } from './services/excluded-pages.service';
import { ViewModeService } from './services/view-mode.service'; import { ViewModeService } from './services/view-mode.service';
@ -362,7 +362,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
); );
let start; let start;
return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe( return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe(
debounceTime(300),
tap(() => (start = new Date().getTime())), tap(() => (start = new Date().getTime())),
map(([annotations]) => annotations), map(([annotations]) => annotations),
startWith([] as AnnotationWrapper[]), startWith([] as AnnotationWrapper[]),

View File

@ -1,6 +1,6 @@
import { Injectable, Injector } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { combineLatest, firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchMap } from 'rxjs'; import { combineLatest, firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchMap } from 'rxjs';
import { Dossier, DOSSIER_ID, File, FILE_ID } from '@red/domain'; import { Dictionary, Dossier, DOSSIER_ID, File, FILE_ID } from '@red/domain';
import { ActivatedRoute, Router } from '@angular/router'; import { ActivatedRoute, Router } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesMapService } from '@services/entity-services/files-map.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
@ -11,6 +11,7 @@ import { dossiersServiceResolver } from '@services/entity-services/dossiers.serv
import { wipeFilesCache } from '@red/cache'; import { wipeFilesCache } from '@red/cache';
import { DossiersService } from '@services/dossiers/dossiers.service'; import { DossiersService } from '@services/dossiers/dossiers.service';
import { FilesService } from '@services/entity-services/files.service'; import { FilesService } from '@services/entity-services/files.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
@Injectable() @Injectable()
export class FilePreviewStateService { export class FilePreviewStateService {
@ -24,10 +25,9 @@ export class FilePreviewStateService {
readonly dossierId: string; readonly dossierId: string;
readonly dossierTemplateId: string; readonly dossierTemplateId: string;
readonly fileId: string; readonly fileId: string;
dossier: Dossier; dossier: Dossier;
file: File; file: File;
#dossierDictionary: Dictionary;
readonly #reloadBlob$ = new Subject(); readonly #reloadBlob$ = new Subject();
constructor( constructor(
@ -39,6 +39,7 @@ export class FilePreviewStateService {
private readonly _filesService: FilesService, private readonly _filesService: FilesService,
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _fileManagementService: FileManagementService, private readonly _fileManagementService: FileManagementService,
private readonly _dictionaryService: DictionaryService,
) { ) {
const dossiersService = dossiersServiceResolver(_injector, router); const dossiersService = dossiersServiceResolver(_injector, router);
@ -55,6 +56,14 @@ export class FilePreviewStateService {
this.blob$ = this.#blob$; this.blob$ = this.#blob$;
this.dossierFileChange$ = this.#dossierFilesChange$(); this.dossierFileChange$ = this.#dossierFilesChange$();
this._dictionaryService
.getDossierDictionary(this.dossierTemplateId, this.dossierId)
.subscribe(dictionary => (this.#dossierDictionary = dictionary));
}
get dossierDictionary(): Dictionary {
return this.#dossierDictionary;
} }
get blob(): Promise<Blob> { get blob(): Promise<Blob> {

View File

@ -13,12 +13,13 @@ export class MultiSelectService {
readonly enabled$: Observable<boolean>; readonly enabled$: Observable<boolean>;
readonly active$: Observable<boolean>; readonly active$: Observable<boolean>;
readonly inactive$: Observable<boolean>; readonly inactive$: Observable<boolean>;
readonly #active$ = new BehaviorSubject(false); readonly #active$ = new BehaviorSubject(false);
readonly #enabled$ = new BehaviorSubject(true); readonly #enabled$ = new BehaviorSubject(true);
constructor(private readonly _viewModeService: ViewModeService, private readonly _state: FilePreviewStateService) { constructor(viewModeService: ViewModeService, state: FilePreviewStateService) {
[this.active$, this.inactive$] = boolFactory(this.#active$.asObservable()); [this.active$, this.inactive$] = boolFactory(this.#active$.asObservable());
this.enabled$ = combineLatest([this._viewModeService.viewMode$, _state.isWritable$]).pipe( this.enabled$ = combineLatest([viewModeService.viewMode$, state.isWritable$]).pipe(
map(([viewMode, isWritable]) => isWritable && ENABLED_MULTISELECT.includes(viewMode)), map(([viewMode, isWritable]) => isWritable && ENABLED_MULTISELECT.includes(viewMode)),
tap(enabled => this.#enabled$.next(enabled)), tap(enabled => this.#enabled$.next(enabled)),
); );

View File

@ -227,7 +227,7 @@ export class PdfViewer {
this.navigateToPage(pageNumber); this.navigateToPage(pageNumber);
// wait for page to be loaded and to draw annotations // wait for page to be loaded and to draw annotations
setTimeout(() => this.#jumpAndSelectAnnotations(filteredAnnotationsIds), 500); setTimeout(() => this.#jumpAndSelectAnnotations(filteredAnnotationsIds), 300);
} }
#jumpAndSelectAnnotations(annotationIds: readonly string[]) { #jumpAndSelectAnnotations(annotationIds: readonly string[]) {

View File

@ -2,10 +2,11 @@ import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import packageInfo from '../../../../../package.json'; import packageInfo from '../../../../../package.json';
import config from '../../assets/config/config.json'; import envConfig from '../../assets/config/config.json';
import { CacheApiService, wipeCaches } from '@red/cache'; import { CacheApiService, wipeCaches } from '@red/cache';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { AppConfig } from '@red/domain';
const version = packageInfo.version; const version = packageInfo.version;
@ -19,19 +20,21 @@ export class ConfigService {
private readonly _titleService: Title, private readonly _titleService: Title,
) { ) {
this._checkFrontendVersion(); this._checkFrontendVersion();
console.log('[REDACTION] Started with config: ', this._values);
} }
private _values = { ...config, FRONTEND_APP_VERSION: version } as const; private _values: AppConfig = { ...envConfig, FRONTEND_APP_VERSION: version } as const;
get values() { get values() {
return this._values; return this._values;
} }
loadAppConfig(): Observable<any> { loadAppConfig(): Observable<any> {
return this._httpClient.get<any>('/assets/config/config.json').pipe( return this._httpClient.get('/app-config').pipe(
tap(envConfig => { tap(appConfig => {
console.log('[REDACTION] Started with config: ', envConfig); console.log('[REDACTION] Loaded app config: ', appConfig);
this._values = envConfig; this._values = { ...this._values, ...appConfig };
}), }),
); );
} }

View File

@ -1,6 +1,6 @@
import { Injectable, Injector } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { firstValueFrom, forkJoin, Observable, of, throwError } from 'rxjs'; import { firstValueFrom, forkJoin, Observable, of, throwError } from 'rxjs';
import { EntitiesService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; import { EntitiesService, List, log, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IColors, IDictionary, IUpdateDictionary } from '@red/domain'; import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IColors, IDictionary, IUpdateDictionary } from '@red/domain';
import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -174,7 +174,7 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
async getDictionariesOptions(dossierTemplateId: string, dossierId: string): Promise<Dictionary[]> { async getDictionariesOptions(dossierTemplateId: string, dossierId: string): Promise<Dictionary[]> {
const possibleDictionaries: Dictionary[] = []; const possibleDictionaries: Dictionary[] = [];
const dossierDictionary = await firstValueFrom(this.getForType(dossierTemplateId, 'dossier_redaction', dossierId)); const dossierDictionary: Dictionary = await firstValueFrom(this.getDossierDictionary(dossierTemplateId, dossierId));
for (const dictionary of this._dictionariesMapService.get(dossierTemplateId)) { for (const dictionary of this._dictionariesMapService.get(dossierTemplateId)) {
if (!dictionary.virtual && dictionary.addToDictionaryAction) { if (!dictionary.virtual && dictionary.addToDictionaryAction) {
@ -183,8 +183,7 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
} }
if (dossierDictionary.addToDictionaryAction) { if (dossierDictionary.addToDictionaryAction) {
// TODO fix this in the backend possibleDictionaries.push(dossierDictionary);
possibleDictionaries.push(new Dictionary({ ...dossierDictionary, type: 'dossier_redaction' }));
} }
possibleDictionaries.sort((a, b) => a.label.localeCompare(b.label)); possibleDictionaries.sort((a, b) => a.label.localeCompare(b.label));
@ -192,6 +191,18 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
return possibleDictionaries; return possibleDictionaries;
} }
getDossierDictionary(dossierTemplateId: string, dossierId: string): Observable<Dictionary> {
return this.getForType(dossierTemplateId, 'dossier_redaction', dossierId).pipe(
map(
dictionary =>
new Dictionary({
...dictionary,
type: 'dossier_redaction',
}),
),
);
}
loadDictionaryData(dossierTemplatesIds: string[]): Observable<Dictionary[][]> { loadDictionaryData(dossierTemplatesIds: string[]): Observable<Dictionary[][]> {
const observables: Observable<Dictionary[]>[] = []; const observables: Observable<Dictionary[]>[] = [];
for (const dossierTemplateId of dossierTemplatesIds) { for (const dossierTemplateId of dossierTemplatesIds) {

View File

@ -64,7 +64,7 @@ export class TrashService extends EntitiesService<TrashItem> {
dossier => dossier =>
new TrashDossier( new TrashDossier(
dossier, dossier,
this._configService.values.DELETE_RETENTION_HOURS as number, this._configService.values.softDeleteCleanupTime,
this._permissionsService.canRestoreDossier(dossier), this._permissionsService.canRestoreDossier(dossier),
this._permissionsService.canHardDeleteDossier(dossier), this._permissionsService.canHardDeleteDossier(dossier),
), ),
@ -82,7 +82,7 @@ export class TrashService extends EntitiesService<TrashItem> {
return new TrashFile( return new TrashFile(
file, file,
dossier.dossierTemplateId, dossier.dossierTemplateId,
this._configService.values.DELETE_RETENTION_HOURS as number, this._configService.values.softDeleteCleanupTime,
this._permissionsService.canRestoreFile(file, dossier), this._permissionsService.canRestoreFile(file, dossier),
this._permissionsService.canHardDeleteFile(file, dossier), this._permissionsService.canHardDeleteFile(file, dossier),
); );

View File

@ -39,6 +39,7 @@ export function configurationInitializer(
switchMap(user => (!user.hasAnyREDRoles ? throwError('Not user has no red roles') : of({}))), switchMap(user => (!user.hasAnyREDRoles ? throwError('Not user has no red roles') : of({}))),
mergeMap(() => generalSettingsService.getGeneralConfigurations()), mergeMap(() => generalSettingsService.getGeneralConfigurations()),
tap(configuration => configService.updateDisplayName(configuration.displayName)), tap(configuration => configService.updateDisplayName(configuration.displayName)),
switchMap(() => configService.loadAppConfig()),
switchMap(() => userPreferenceService.reload()), switchMap(() => userPreferenceService.reload()),
catchError(e => { catchError(e => {
console.log('[Redaction] Initialization error:', e); console.log('[Redaction] Initialization error:', e);

View File

@ -5,7 +5,6 @@
"APP_NAME": "RedactManager", "APP_NAME": "RedactManager",
"AUTO_READ_TIME": 3, "AUTO_READ_TIME": 3,
"BACKEND_APP_VERSION": "4.4.40", "BACKEND_APP_VERSION": "4.4.40",
"DELETE_RETENTION_HOURS": 96,
"EULA_URL": "EULA_URL", "EULA_URL": "EULA_URL",
"FRONTEND_APP_VERSION": "1.1", "FRONTEND_APP_VERSION": "1.1",
"LICENSE_CUSTOMER": "Development License", "LICENSE_CUSTOMER": "Development License",

View File

@ -83,12 +83,6 @@
"it": "", "it": "",
"fr": "" "fr": ""
}, },
"dossiers_quickfilter_my_dossiers": {
"en": "/en/index-en.html?contextId=dossiers_quickfilter_my_dossiers",
"de": "",
"it": "",
"fr": ""
},
"edit_dossier_in_dossier": { "edit_dossier_in_dossier": {
"en": "/en/index-en.html?contextId=edit_dossier_in_dossier", "en": "/en/index-en.html?contextId=edit_dossier_in_dossier",
"de": "", "de": "",

View File

@ -6,7 +6,6 @@ API_URL="${API_URL:-/redaction-gateway-v1}"
APP_NAME="${APP_NAME:-}" APP_NAME="${APP_NAME:-}"
AUTO_READ_TIME="${AUTO_READ_TIME:-1.5}" AUTO_READ_TIME="${AUTO_READ_TIME:-1.5}"
BACKEND_APP_VERSION="${BACKEND_APP_VERSION:-4.7.0}" BACKEND_APP_VERSION="${BACKEND_APP_VERSION:-4.7.0}"
DELETE_RETENTION_HOURS="${DELETE_RETENTION_HOURS:-96}"
EULA_URL="${EULA_URL:-}" EULA_URL="${EULA_URL:-}"
FRONTEND_APP_VERSION="${FRONTEND_APP_VERSION:-}" FRONTEND_APP_VERSION="${FRONTEND_APP_VERSION:-}"
@ -34,7 +33,6 @@ echo '{
"APP_NAME":"'"$APP_NAME"'", "APP_NAME":"'"$APP_NAME"'",
"AUTO_READ_TIME":'"$AUTO_READ_TIME"', "AUTO_READ_TIME":'"$AUTO_READ_TIME"',
"BACKEND_APP_VERSION":"'"$BACKEND_APP_VERSION"'", "BACKEND_APP_VERSION":"'"$BACKEND_APP_VERSION"'",
"DELETE_RETENTION_HOURS":'"$DELETE_RETENTION_HOURS"',
"EULA_URL":"'"$EULA_URL:"'", "EULA_URL":"'"$EULA_URL:"'",
"FRONTEND_APP_VERSION":"'"$FRONTEND_APP_VERSION:"'", "FRONTEND_APP_VERSION":"'"$FRONTEND_APP_VERSION:"'",
"LICENSE_EMAIL":"'"$LICENSE_EMAIL"'", "LICENSE_EMAIL":"'"$LICENSE_EMAIL"'",

@ -1 +1 @@
Subproject commit f90405572f662bb150950032f726305a616de0dc Subproject commit e7be61efff0b9baa3d6b53002cbecd8b53d7b934

View File

@ -0,0 +1,26 @@
export interface AppConfig {
ADMIN_CONTACT_NAME: string;
ADMIN_CONTACT_URL: string;
API_URL: string;
APP_NAME: string;
AUTO_READ_TIME: number;
BACKEND_APP_VERSION: string;
EULA_URL: string;
FRONTEND_APP_VERSION: string;
LICENSE_CUSTOMER: string;
LICENSE_EMAIL: string;
LICENSE_END: string;
LICENSE_PAGE_COUNT: number;
LICENSE_START: string;
MAX_FILE_SIZE_MB: number;
MAX_RETRIES_ON_SERVER_ERROR: number;
OAUTH_CLIENT_ID: string;
OAUTH_IDP_HINT: null;
OAUTH_URL: string;
RECENT_PERIOD_IN_HOURS: number;
SELECTION_MODE: string;
MANUAL_BASE_URL: string;
softDeleteCleanupTime?: number;
downloadCleanupDownloadFilesHours?: number;
downloadCleanupNotDownloadFilesHours?: number;
}

View File

@ -11,3 +11,4 @@ export * from './pdf.types';
export * from './logger-config'; export * from './logger-config';
export * from './admin-side-nav-types'; export * from './admin-side-nav-types';
export * from './charts'; export * from './charts';
export * from './app-config';

View File

@ -1,6 +1,6 @@
{ {
"name": "redaction", "name": "redaction",
"version": "3.479.0", "version": "3.486.0",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {

View File

@ -31,6 +31,10 @@
.publication-contents { .publication-contents {
@include mixin.card; @include mixin.card;
border: none;
background-color: transparent;
width: 250px; width: 250px;
margin: 0 15px !important; margin: 0 15px !important;

Binary file not shown.

View File

@ -1,5 +1,5 @@
@charset "UTF-8"; @charset "UTF-8";
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap"); @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap');
.portal-header { .portal-header {
z-index: 1; z-index: 1;
height: 450px; height: 450px;
@ -88,6 +88,8 @@
margin: 0; margin: 0;
background-color: #fff; background-color: #fff;
border-radius: 4px; border-radius: 4px;
border: none;
background-color: transparent;
width: 250px; width: 250px;
margin: 0 15px !important; margin: 0 15px !important;
} }
@ -142,72 +144,72 @@
/* Einleitung */ /* Einleitung */
.cat-panel-1:before { .cat-panel-1:before {
content: "\f277"; content: '\f277';
} }
/* Workflow */ /* Workflow */
.cat-panel-2:before { .cat-panel-2:before {
content: "\f0c1"; content: '\f0c1';
} }
/* Voraussetzungen */ /* Voraussetzungen */
.cat-panel-3:before { .cat-panel-3:before {
content: "\f109"; content: '\f109';
} }
/* Benutzermenü und -profil */ /* Benutzermenü und -profil */
.cat-panel-4:before { .cat-panel-4:before {
content: "\f007"; content: '\f007';
} }
/* Benachrichtigungen */ /* Benachrichtigungen */
.cat-panel-5:before { .cat-panel-5:before {
content: "\f0f3"; content: '\f0f3';
} }
/* Suchfunktion */ /* Suchfunktion */
.cat-panel-6:before { .cat-panel-6:before {
content: "\f002"; content: '\f002';
} }
/* Ebenen in der Benutzeroberfläche des RedactManagers */ /* Ebenen in der Benutzeroberfläche des RedactManagers */
.cat-panel-7:before { .cat-panel-7:before {
content: "\f248"; content: '\f248';
} }
/* Rollen und Berechtigungen */ /* Rollen und Berechtigungen */
.cat-panel-8:before { .cat-panel-8:before {
content: "\f084"; content: '\f084';
} }
/* Dossier erstellen und verwalten */ /* Dossier erstellen und verwalten */
.cat-panel-9:before { .cat-panel-9:before {
content: "\f07c"; content: '\f07c';
} }
/* Dokumente bearbeiten im Editor */ /* Dokumente bearbeiten im Editor */
.cat-panel-10:before { .cat-panel-10:before {
content: "\f15c"; content: '\f15c';
} }
/* Dossier abschließen und herunterladen */ /* Dossier abschließen und herunterladen */
.cat-panel-11:before { .cat-panel-11:before {
content: "\f019"; content: '\f019';
} }
/* Funktionsübersicht */ /* Funktionsübersicht */
.cat-panel-12:before { .cat-panel-12:before {
content: "\f03a"; content: '\f03a';
} }
/* Glossar */ /* Glossar */
.cat-panel-13:before { .cat-panel-13:before {
content: "\f02d"; content: '\f02d';
} }
/* FAQs (häufige Fragen) */ /* FAQs (häufige Fragen) */
.cat-panel-14:before { .cat-panel-14:before {
content: "\f059"; content: '\f059';
} }
.portal-search-result { .portal-search-result {
@ -643,22 +645,28 @@ main ul {
body { body {
color: #283241; color: #283241;
background-color: #f5f5f7; background-color: #f5f5f7;
font-family: "Open Sans", sans-serif; font-family: 'Open Sans', sans-serif;
scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2); scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2);
scrollbar-width: thin; scrollbar-width: thin;
/* Track */ /* Track */
/* Handle */ /* Handle */
} }
body h1, body .h1, body h1,
body h2, body .h2, body .h1,
body h3, body .h3, body h2,
body h4, body .h4, body .h2,
body h5, body .h5, body h3,
body h6, body .h6, body .h3,
body h4,
body .h4,
body h5,
body .h5,
body h6,
body .h6,
body p, body p,
body pre { body pre {
margin: 0; margin: 0;
font-family: "Open Sans", sans-serif; font-family: 'Open Sans', sans-serif;
} }
body::-webkit-scrollbar { body::-webkit-scrollbar {
width: 11px; width: 11px;
@ -669,27 +677,32 @@ body::-webkit-scrollbar-track {
body::-webkit-scrollbar-thumb { body::-webkit-scrollbar-thumb {
background: var(--iqser-quick-filter-border); background: var(--iqser-quick-filter-border);
} }
body h1, body .h1 { body h1,
body .h1 {
font-size: 64px; font-size: 64px;
font-weight: 300; font-weight: 300;
line-height: 87px; line-height: 87px;
} }
body h2, body .h2 { body h2,
body .h2 {
font-size: 42px; font-size: 42px;
font-weight: 300; font-weight: 300;
line-height: 57px; line-height: 57px;
} }
body h3, body .h3 { body h3,
body .h3 {
font-size: 32px; font-size: 32px;
font-weight: 300; font-weight: 300;
line-height: 43px; line-height: 43px;
} }
body h4, body .h4 { body h4,
body .h4 {
font-size: 28px; font-size: 28px;
font-weight: 300; font-weight: 300;
line-height: 36px; line-height: 36px;
} }
body h5, body .h5 { body h5,
body .h5 {
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 24px; line-height: 24px;