+
(undefined);
- protected readonly componentLogData$ = toObservable(this.componentLogData);
protected readonly iconButtonTypes = IconButtonTypes;
- protected displayedComponents$: Observable;
@Input() file: File;
@Input() dictionaries: Dictionary[];
@ViewChildren('editableComponent') editableComponents: List;
constructor(
- private readonly _componentLogService: ComponentLogService,
private readonly _loadingService: LoadingService,
private readonly _componentLogFilterService: ComponentLogFilterService,
private readonly _filterService: FilterService,
private readonly _state: FilePreviewStateService,
+ protected readonly componentLogService: ComponentLogService,
) {
effect(async () => {
this._state.file();
@@ -48,7 +43,6 @@ export class StructuredComponentManagementComponent implements OnInit {
async ngOnInit(): Promise {
await this.#loadData();
- this.displayedComponents$ = this.#displayedComponents$();
}
deselectLast() {
@@ -61,7 +55,7 @@ export class StructuredComponentManagementComponent implements OnInit {
async revertOverride(originalKey: string) {
this._loadingService.start();
await firstValueFrom(
- this._componentLogService.revertOverride(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, [originalKey]),
+ this.componentLogService.revertOverride(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, [originalKey]),
);
await this.#loadData();
}
@@ -69,29 +63,21 @@ export class StructuredComponentManagementComponent implements OnInit {
async overrideValue(componentLogEntry: IComponentLogEntry) {
this._loadingService.start();
await firstValueFrom(
- this._componentLogService.override(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, componentLogEntry),
+ this.componentLogService.override(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId, componentLogEntry),
);
await this.#loadData();
}
- #displayedComponents$() {
- const componentLogFilters$ = this._filterService.getFilterModels$('componentLogFilters');
- return combineLatest([this.componentLogData$, componentLogFilters$]).pipe(
- map(([components, filters]) => this._componentLogFilterService.filterComponents(components, filters)),
- );
- }
-
async #loadData(): Promise {
- const componentLogData = await firstValueFrom(
- this._componentLogService.getComponentLogData(
+ await firstValueFrom(
+ this.componentLogService.loadComponentLogData(
this.file.dossierTemplateId,
this.file.dossierId,
this.file.fileId,
this.dictionaries,
),
);
- this.#computeFilters(componentLogData);
- this.componentLogData.set(componentLogData);
+ this.#computeFilters(this.componentLogService.all);
this._loadingService.stop();
}
diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
index a6f5aefff..ab7ed93b1 100644
--- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
@@ -17,7 +17,15 @@ import {
LoadingService,
Toaster,
} from '@iqser/common-ui';
-import { copyLocalStorageFiltersValues, FilterService, NestedFilter, processFilters } from '@iqser/common-ui/lib/filtering';
+import {
+ copyLocalStorageFiltersValues,
+ Filter,
+ FilterService,
+ IFilter,
+ INestedFilter,
+ NestedFilter,
+ processFilters,
+} from '@iqser/common-ui/lib/filtering';
import { AutoUnsubscribe, Bind, bool, List, OnAttach, OnDetach } from '@iqser/common-ui/lib/utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ManualRedactionEntryTypes, ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
@@ -73,6 +81,7 @@ import { StructuredComponentManagementComponent } from './components/structured-
import { DocumentInfoService } from './services/document-info.service';
import { RectangleAnnotationDialog } from './dialogs/rectangle-annotation-dialog/rectangle-annotation-dialog.component';
import { ANNOTATION_ACTION_ICONS, ANNOTATION_ACTIONS } from './utils/constants';
+import { ComponentLogService } from '@services/entity-services/component-log.service';
@Component({
templateUrl: './file-preview-screen.component.html',
@@ -147,6 +156,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _multiSelectService: MultiSelectService,
private readonly _documentInfoService: DocumentInfoService,
+ private readonly _componentLogService: ComponentLogService,
) {
super();
effect(() => {
@@ -197,6 +207,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._documentInfoService.shown();
this.#updateViewerPosition();
});
+
+ effect(() => {
+ this.state.componentReferenceIds();
+ this.#rebuildFilters();
+ });
}
get changed() {
@@ -538,8 +553,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
#rebuildFilters() {
const startTime = new Date().getTime();
- const annotationFilters = this._annotationProcessingService.getAnnotationFilter();
+ let annotationFilters = this._annotationProcessingService.getAnnotationFilter();
const primaryFilters = this._filterService.getGroup('primaryFilters')?.filters;
+
+ if (this.isDocumine) {
+ annotationFilters = this.#filterAnnotationFilters(annotationFilters);
+ }
+
this._filterService.addFilterGroup({
slug: 'primaryFilters',
filterTemplate: this._filterTemplate,
@@ -832,6 +852,38 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
};
}
+ #filterAnnotationFilters(annotationFilters: INestedFilter[]) {
+ const components = this._componentLogService.all;
+ const filteredComponentIds = untracked(this.state.componentReferenceIds);
+
+ if (filteredComponentIds && annotationFilters) {
+ const filteredComponentIdsSet = new Set(filteredComponentIds);
+
+ const references = new Set();
+ for (const component of components) {
+ for (const componentValue of component.componentValues) {
+ for (const reference of componentValue.entityReferences) {
+ if (filteredComponentIdsSet.has(reference.id)) {
+ references.add(reference.type);
+ }
+ }
+ }
+ }
+
+ return annotationFilters
+ .map(filter => {
+ const filteredChildren = filter.children.filter(c => references.has(c.label.replace(/ /g, '_').toLowerCase()));
+ if (filteredChildren.length) {
+ return { ...filter, children: filteredChildren };
+ }
+ return null;
+ })
+ .filter(f => f !== null);
+ }
+
+ return annotationFilters;
+ }
+
#updateViewerPosition() {
if (this.isDocumine) {
if (this._documentInfoService.shown()) {
diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
index ff821733f..aac95d66c 100644
--- a/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
+++ b/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
@@ -1,6 +1,6 @@
import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angular/common/http';
import { computed, effect, inject, Injectable, signal, Signal, WritableSignal } from '@angular/core';
-import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
+import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
import { LoadingService, wipeCache } from '@iqser/common-ui';
import { getParam } from '@iqser/common-ui/lib/utils';
import { TranslateService } from '@ngx-translate/core';
@@ -14,7 +14,7 @@ import { FilesMapService } from '@services/files/files-map.service';
import { FilesService } from '@services/files/files.service';
import { PermissionsService } from '@services/permissions.service';
import { NGXLogger } from 'ngx-logger';
-import { BehaviorSubject, firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchMap } from 'rxjs';
+import { firstValueFrom, from, merge, Observable, of, pairwise, Subject, switchMap } from 'rxjs';
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { ViewModeService } from './view-mode.service';
@@ -42,7 +42,7 @@ export class FilePreviewStateService {
readonly dossierDictionary: Signal;
readonly blob$: Observable;
readonly componentReferenceIds$: Observable;
- readonly #componentReferenceIds$ = new BehaviorSubject(null);
+ readonly componentReferenceIds = signal([]);
readonly dossierId = getParam(DOSSIER_ID);
readonly dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
readonly fileId = getParam(FILE_ID);
@@ -64,7 +64,7 @@ export class FilePreviewStateService {
this.dossier = toSignal(dossiersServiceResolver().getEntityChanged$(this.dossierId));
this.file$ = inject(FilesMapService).watch$(this.dossierId, this.fileId);
this.file = toSignal(this.file$);
- this.componentReferenceIds$ = this.#componentReferenceIds$.asObservable();
+ this.componentReferenceIds$ = toObservable(this.componentReferenceIds);
this.excludedPages = signal(this.file().excludedPages);
this.isWritable = computed(() => {
const isWritable = this._permissionsService.canPerformAnnotationActions(this.file(), this.dossier());
@@ -94,10 +94,6 @@ export class FilePreviewStateService {
);
}
- set componentReferenceIds(ids: string[]) {
- this.#componentReferenceIds$.next(ids);
- }
-
get dictionaries(): Dictionary[] {
const dictionaries = this._dictionariesMapService.get(this.dossierTemplateId);
if (this.dossierDictionary()) {
@@ -134,10 +130,6 @@ export class FilePreviewStateService {
);
}
- get componentReferenceIds() {
- return this.#componentReferenceIds$.getValue();
- }
-
reloadBlob(): void {
this.#reloadBlob$.next(true);
}
diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
index 1107af8b2..d6265e542 100644
--- a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts
@@ -9,7 +9,7 @@ import { APP_BASE_HREF } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { MatMenu, MatMenuItem, MatMenuTrigger } from '@angular/material/menu';
import { firstValueFrom } from 'rxjs';
-import { ComponentLogService } from '@services/files/component-log.service';
+import { ComponentLogService } from '@services/entity-services/component-log.service';
@Component({
selector: 'redaction-file-download-btn',
diff --git a/apps/red-ui/src/app/services/files/component-log.service.ts b/apps/red-ui/src/app/services/entity-services/component-log.service.ts
similarity index 95%
rename from apps/red-ui/src/app/services/files/component-log.service.ts
rename to apps/red-ui/src/app/services/entity-services/component-log.service.ts
index 49235895e..e08c91607 100644
--- a/apps/red-ui/src/app/services/files/component-log.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/component-log.service.ts
@@ -1,15 +1,14 @@
import { Injectable } from '@angular/core';
-import { GenericService } from '@iqser/common-ui';
-import { catchError, map, tap } from 'rxjs/operators';
-import { Observable, of } from 'rxjs';
-import { HttpHeaders } from '@angular/common/http';
-import { saveAs } from 'file-saver';
+import { EntitiesService } from '@iqser/common-ui';
import { ComponentDetails, ComponentLogEntry, Dictionary, IComponentLogData, IComponentLogEntry, IFile } from '@red/domain';
+import { Observable, of } from 'rxjs';
+import { catchError, map, tap } from 'rxjs/operators';
import { mapEach } from '@common-ui/utils';
-import { FilePreviewStateService } from '../../modules/file-preview/services/file-preview-state.service';
+import { saveAs } from 'file-saver';
+import { HttpHeaders } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
-export class ComponentLogService extends GenericService {
+export class ComponentLogService extends EntitiesService {
protected readonly _defaultModelPath = '';
#componentLogRequest(
@@ -35,7 +34,7 @@ export class ComponentLogService extends GenericService {
);
}
- getComponentLogData(
+ loadComponentLogData(
dossierTemplateId: string,
dossierId: string,
fileId: string,
@@ -47,6 +46,7 @@ export class ComponentLogService extends GenericService {
map(componentDetails => this.#mapComponentDetails(componentDetails)),
mapEach(log => new ComponentLogEntry(log)),
map(log => this.#updateDisplayValue(log, dictionaries)),
+ tap(log => this.setEntities(log)),
);
}
diff --git a/libs/red-domain/src/lib/component-log/component-log-entry.ts b/libs/red-domain/src/lib/component-log/component-log-entry.ts
index 1a53a3bb5..a6ca39a2a 100644
--- a/libs/red-domain/src/lib/component-log/component-log-entry.ts
+++ b/libs/red-domain/src/lib/component-log/component-log-entry.ts
@@ -1,4 +1,5 @@
import { ComponentValue, IComponentValue } from './component-value';
+import { IListable } from '@iqser/common-ui';
export type ComponentDetails = Record>;
@@ -9,16 +10,22 @@ export interface IComponentLogEntry {
overridden?: boolean;
}
-export class ComponentLogEntry implements IComponentLogEntry {
+export class ComponentLogEntry implements IComponentLogEntry, IListable {
+ readonly id: string;
readonly name: string;
readonly originalKey: string;
readonly componentValues: ComponentValue[];
readonly overridden: boolean;
constructor(entry: IComponentLogEntry) {
+ this.id = entry.name;
this.name = entry.name;
this.originalKey = entry.name;
this.componentValues = entry.componentValues;
this.overridden = !!entry.overridden;
}
+
+ get searchKey(): string {
+ return this.originalKey;
+ }
}
diff --git a/libs/red-domain/src/lib/watermarks/watermark.model.ts b/libs/red-domain/src/lib/watermarks/watermark.model.ts
index adb9a11e1..bff94f7e9 100644
--- a/libs/red-domain/src/lib/watermarks/watermark.model.ts
+++ b/libs/red-domain/src/lib/watermarks/watermark.model.ts
@@ -2,7 +2,6 @@ import {
IWatermark,
WATERMARK_HORIZONTAL_ALIGNMENTS,
WATERMARK_VERTICAL_ALIGNMENTS,
- WatermarkAlignment,
WatermarkHorizontalAlignment,
WatermarkOrientation,
WatermarkVerticalAlignment,