Merge branch 'VM/RED-9996' into 'master'
RED-9996 - pre-filter available annotation filter types in new DocuMine component view Closes RED-9996 See merge request redactmanager/red-ui!706
This commit is contained in:
commit
edadf1ea41
@ -1,10 +1,10 @@
|
||||
import { Component, input, Input } from '@angular/core';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { ComponentLogService } from '@services/files/component-log.service';
|
||||
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';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-documine-export',
|
||||
|
||||
@ -88,7 +88,7 @@ export class EditableStructuredComponentValueComponent implements OnInit {
|
||||
}
|
||||
this.deselectLast.emit();
|
||||
this.selected = true;
|
||||
this._state.componentReferenceIds = this.#getUniqueReferencesIds(this.currentEntry().componentValues);
|
||||
this._state.componentReferenceIds.set(this.#getUniqueReferencesIds(this.currentEntry().componentValues));
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ export class EditableStructuredComponentValueComponent implements OnInit {
|
||||
$event?.stopImmediatePropagation();
|
||||
this.selected = false;
|
||||
this.editing = false;
|
||||
this._state.componentReferenceIds = null;
|
||||
this._state.componentReferenceIds.set(null);
|
||||
}
|
||||
|
||||
cancel($event?: MouseEvent) {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<iqser-popup-filter [primaryFiltersSlug]="'componentLogFilters'" [attr.help-mode-key]="'filter_components'"></iqser-popup-filter>
|
||||
</div>
|
||||
|
||||
<div *ngIf="displayedComponents$ | async as displayedComponents" class="components-container" id="components-view">
|
||||
<div *ngIf="componentLogService.all$ | async as components" class="components-container" id="components-view">
|
||||
<div class="component-row">
|
||||
<div class="header">
|
||||
<div class="component">{{ 'component-management.table-header.component' | translate }}</div>
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="row-separator"></div>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let entry of displayedComponents" class="component-row">
|
||||
<div *ngFor="let entry of components" class="component-row">
|
||||
<redaction-editable-structured-component-value
|
||||
#editableComponent
|
||||
[entry]="entry"
|
||||
|
||||
@ -2,16 +2,14 @@ import { Component, effect, Input, OnInit, signal, ViewChildren } from '@angular
|
||||
import { List } from '@common-ui/utils';
|
||||
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { ComponentLogEntry, Dictionary, File, IComponentLogEntry, WorkflowFileStatuses } from '@red/domain';
|
||||
import { ComponentLogService } from '@services/files/component-log.service';
|
||||
import { combineLatest, firstValueFrom, Observable } from 'rxjs';
|
||||
import { EditableStructuredComponentValueComponent } from '../editable-structured-component-value/editable-structured-component-value.component';
|
||||
import { FilterService, PopupFilterComponent } from '@common-ui/filtering';
|
||||
import { ComponentLogFilterService } from '../../services/component-log-filter.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import { AsyncPipe, NgForOf, NgIf } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { ComponentLogService } from '@services/entity-services/component-log.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-structured-component-management',
|
||||
@ -21,20 +19,17 @@ import { FilePreviewStateService } from '../../services/file-preview-state.servi
|
||||
imports: [PopupFilterComponent, NgIf, AsyncPipe, TranslateModule, NgForOf, EditableStructuredComponentValueComponent],
|
||||
})
|
||||
export class StructuredComponentManagementComponent implements OnInit {
|
||||
protected readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
||||
protected readonly componentLogData$ = toObservable(this.componentLogData);
|
||||
protected readonly iconButtonTypes = IconButtonTypes;
|
||||
protected displayedComponents$: Observable<ComponentLogEntry[]>;
|
||||
@Input() file: File;
|
||||
@Input() dictionaries: Dictionary[];
|
||||
@ViewChildren('editableComponent') editableComponents: List<EditableStructuredComponentValueComponent>;
|
||||
|
||||
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<void> {
|
||||
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<void> {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@ -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<string>();
|
||||
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()) {
|
||||
|
||||
@ -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<Dictionary>;
|
||||
readonly blob$: Observable<Blob>;
|
||||
readonly componentReferenceIds$: Observable<string[] | null>;
|
||||
readonly #componentReferenceIds$ = new BehaviorSubject<string[] | null>(null);
|
||||
readonly componentReferenceIds = signal<string[]>([]);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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<void> {
|
||||
export class ComponentLogService extends EntitiesService<IComponentLogEntry, ComponentLogEntry> {
|
||||
protected readonly _defaultModelPath = '';
|
||||
|
||||
#componentLogRequest(
|
||||
@ -35,7 +34,7 @@ export class ComponentLogService extends GenericService<void> {
|
||||
);
|
||||
}
|
||||
|
||||
getComponentLogData(
|
||||
loadComponentLogData(
|
||||
dossierTemplateId: string,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
@ -47,6 +46,7 @@ export class ComponentLogService extends GenericService<void> {
|
||||
map(componentDetails => this.#mapComponentDetails(componentDetails)),
|
||||
mapEach(log => new ComponentLogEntry(log)),
|
||||
map(log => this.#updateDisplayValue(log, dictionaries)),
|
||||
tap(log => this.setEntities(log)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { ComponentValue, IComponentValue } from './component-value';
|
||||
import { IListable } from '@iqser/common-ui';
|
||||
|
||||
export type ComponentDetails = Record<string, Record<'componentValues', IComponentValue>>;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import {
|
||||
IWatermark,
|
||||
WATERMARK_HORIZONTAL_ALIGNMENTS,
|
||||
WATERMARK_VERTICAL_ALIGNMENTS,
|
||||
WatermarkAlignment,
|
||||
WatermarkHorizontalAlignment,
|
||||
WatermarkOrientation,
|
||||
WatermarkVerticalAlignment,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user