RED-8748 - filtered components using popup filter, filtered annotations based on selected component, wip on revert value dialog
This commit is contained in:
parent
cae3f2dec3
commit
c2f40a8d50
@ -46,7 +46,7 @@
|
||||
<div class="flex right">
|
||||
<iqser-circle-button
|
||||
*ngIf="entry.componentValues[0].value !== entry.componentValues[0].originalValue && canEdit"
|
||||
(action)="undo(entry.originalKey)"
|
||||
(action)="undo()"
|
||||
[showDot]="true"
|
||||
[tooltip]="'component-management.actions.undo' | translate"
|
||||
class="undo-value"
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { ComponentLogEntry } from '@red/domain';
|
||||
import { FormBuilder, FormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { BaseFormComponent } from '@iqser/common-ui';
|
||||
import { BaseFormComponent, IqserDialog } from '@iqser/common-ui';
|
||||
import { FilterService } from '@common-ui/filtering';
|
||||
import { RevertValueDialogComponent } from '../../dialogs/docu-mine/revert-value-dialog/revert-value-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-editable-structured-component-value [entry] [canEdit]',
|
||||
@ -17,7 +19,11 @@ export class EditableStructuredComponentValueComponent extends BaseFormComponent
|
||||
selected = false;
|
||||
editing = false;
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder) {
|
||||
constructor(
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _filtersService: FilterService,
|
||||
private readonly _iqserDialog: IqserDialog,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -34,6 +40,7 @@ export class EditableStructuredComponentValueComponent extends BaseFormComponent
|
||||
}
|
||||
this.deselectLast.emit();
|
||||
this.selected = true;
|
||||
this.#setWorkloadFilters();
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +54,7 @@ export class EditableStructuredComponentValueComponent extends BaseFormComponent
|
||||
$event?.stopImmediatePropagation();
|
||||
this.selected = false;
|
||||
this.editing = false;
|
||||
this._filtersService.deactivateFilters({ primaryFiltersSlug: 'primaryFilters' });
|
||||
}
|
||||
|
||||
removeValue(key: string) {
|
||||
@ -57,7 +65,9 @@ export class EditableStructuredComponentValueComponent extends BaseFormComponent
|
||||
const value = this.form.getRawValue();
|
||||
}
|
||||
|
||||
undo(originalKey: string) {}
|
||||
undo() {
|
||||
this._iqserDialog.openDefault(RevertValueDialogComponent);
|
||||
}
|
||||
|
||||
add() {
|
||||
const key = Object.keys(this.form.controls).length.toString();
|
||||
@ -71,4 +81,17 @@ export class EditableStructuredComponentValueComponent extends BaseFormComponent
|
||||
});
|
||||
return form;
|
||||
}
|
||||
|
||||
#setWorkloadFilters() {
|
||||
this._filtersService.deactivateFilters({ primaryFiltersSlug: 'primaryFilters' });
|
||||
|
||||
const filterGroup = this._filtersService.getGroup('primaryFilters');
|
||||
for (const filter of filterGroup.filters) {
|
||||
const nestedFilter = filter.children.find(f => f.label === this.entry.name);
|
||||
if (nestedFilter) {
|
||||
this._filtersService.filterCheckboxClicked({ nestedFilter, filterGroup, primaryFiltersSlug: 'primaryFilters' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<iqser-popup-filter [primaryFiltersSlug]="'componentLogFilters'"></iqser-popup-filter>
|
||||
</div>
|
||||
|
||||
<div *ngIf="componentLogData() as componentLogEntries" class="components-container">
|
||||
<div *ngIf="displayedComponents$ | async as displayedComponents" class="components-container">
|
||||
<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 componentLogEntries" class="component-row">
|
||||
<div *ngFor="let entry of displayedComponents" class="component-row">
|
||||
<redaction-editable-structured-component-value
|
||||
#editableComponent
|
||||
[entry]="entry"
|
||||
|
||||
@ -1,33 +1,31 @@
|
||||
import { Component, Input, signal, ViewChildren } from '@angular/core';
|
||||
import { Component, Input, OnInit, signal, ViewChildren } from '@angular/core';
|
||||
import { ComponentLogEntry, Dictionary, File, WorkflowFileStatuses } from '@red/domain';
|
||||
import { IconButtonTypes, LoadingService } from '@iqser/common-ui';
|
||||
import { ComponentLogService } from '@services/files/component-log.service';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { combineLatest, firstValueFrom, Observable } from 'rxjs';
|
||||
import { List } from '@common-ui/utils';
|
||||
import { EditableStructuredComponentValueComponent } from '../editable-structured-component-value/editable-structured-component-value.component';
|
||||
import { FilterService } from '@common-ui/filtering';
|
||||
import { ComponentLogFilterService } from '../../services/component-log-filter.service';
|
||||
|
||||
interface DeselectEvent {
|
||||
name: string;
|
||||
}
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-structured-component-management',
|
||||
templateUrl: './structured-component-management.component.html',
|
||||
styleUrls: ['/structured-component-management.component.scss'],
|
||||
})
|
||||
export class StructuredComponentManagementComponent {
|
||||
export class StructuredComponentManagementComponent implements OnInit {
|
||||
@Input() file: File;
|
||||
@Input() dictionaries: Dictionary[];
|
||||
|
||||
@ViewChildren('editableComponent') editableComponents: List<EditableStructuredComponentValueComponent>;
|
||||
|
||||
readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
||||
readonly openScmDialogByDefault = signal(this.userPreferences.getOpenScmDialogByDefault());
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
protected readonly componentLogData = signal<ComponentLogEntry[] | undefined>(undefined);
|
||||
protected readonly openScmDialogByDefault = signal(this.userPreferences.getOpenScmDialogByDefault());
|
||||
protected readonly iconButtonTypes = IconButtonTypes;
|
||||
protected displayedComponents$: Observable<ComponentLogEntry[]>;
|
||||
|
||||
constructor(
|
||||
private readonly _componentLogService: ComponentLogService,
|
||||
@ -38,6 +36,24 @@ export class StructuredComponentManagementComponent {
|
||||
readonly userPreferences: UserPreferenceService,
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.#loadData();
|
||||
this.displayedComponents$ = this.#displayedComponents$();
|
||||
}
|
||||
|
||||
#displayedComponents$() {
|
||||
const componentLogData$ = this._componentLogService.getComponentLogData(
|
||||
this.file.dossierTemplateId,
|
||||
this.file.dossierId,
|
||||
this.file.fileId,
|
||||
this.dictionaries,
|
||||
);
|
||||
const componentLogFilters$ = this._filterService.getFilterModels$('componentLogFilters');
|
||||
return combineLatest([componentLogData$, componentLogFilters$]).pipe(
|
||||
map(([components, filters]) => this._componentLogFilterService.filterComponents(components, filters)),
|
||||
);
|
||||
}
|
||||
|
||||
deselectLast() {
|
||||
const lastSelected = this.editableComponents.find(c => c.selected);
|
||||
if (lastSelected) {
|
||||
@ -49,10 +65,6 @@ export class StructuredComponentManagementComponent {
|
||||
return this.file.workflowStatus !== WorkflowFileStatuses.APPROVED;
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.#loadData();
|
||||
}
|
||||
|
||||
getValueCellId(index: number) {
|
||||
return `value-cell-${index}`;
|
||||
}
|
||||
@ -102,30 +114,18 @@ export class StructuredComponentManagementComponent {
|
||||
async #loadData(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
const componentLogData = await firstValueFrom(
|
||||
this._componentLogService.getComponentLogData(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId),
|
||||
this._componentLogService.getComponentLogData(
|
||||
this.file.dossierTemplateId,
|
||||
this.file.dossierId,
|
||||
this.file.fileId,
|
||||
this.dictionaries,
|
||||
),
|
||||
);
|
||||
this.#computeFilters(componentLogData);
|
||||
this.#updateDisplayValue(componentLogData);
|
||||
this.componentLogData.set(componentLogData);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
#updateDisplayValue(componentLogs: ComponentLogEntry[]) {
|
||||
const dictionaries = this.dictionaries;
|
||||
for (const componentLog of componentLogs) {
|
||||
let foundDictionary: Dictionary;
|
||||
for (const reference of componentLog.componentValues[0].entityReferences) {
|
||||
if (foundDictionary) {
|
||||
reference.displayValue = foundDictionary.label;
|
||||
continue;
|
||||
}
|
||||
foundDictionary = dictionaries.find(dict => dict.type === reference.type);
|
||||
foundDictionary = foundDictionary ?? ({ label: reference.type } as Dictionary);
|
||||
reference.displayValue = foundDictionary.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#computeFilters(componentLogs: ComponentLogEntry[]) {
|
||||
const filterGroups = this._componentLogFilterService.filterGroups(componentLogs);
|
||||
this._filterService.addFilterGroups(filterGroups);
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="save()">
|
||||
<div [translate]="'revert-value-dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content"></div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<iqser-icon-button
|
||||
[label]="'revert-value-dialog.actions.revert' | translate"
|
||||
[submit]="true"
|
||||
[type]="iconButtonTypes.primary"
|
||||
/>
|
||||
|
||||
<div [translate]="'revert-value-dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close" />
|
||||
</section>
|
||||
@ -0,0 +1,20 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { CircleButtonComponent, IconButtonComponent, IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { MatDialogClose } from '@angular/material/dialog';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
interface RevertValueData {}
|
||||
interface RevertValueResult {}
|
||||
|
||||
@Component({
|
||||
templateUrl: 'revert-value-dialog.component.html',
|
||||
styleUrls: ['./revert-value-dialog.component.scss'],
|
||||
standalone: true,
|
||||
imports: [CircleButtonComponent, IconButtonComponent, MatDialogClose, TranslateModule],
|
||||
})
|
||||
export class RevertValueDialogComponent extends IqserDialogComponent<RevertValueDialogComponent, RevertValueData, RevertValueResult> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
save() {}
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ComponentLogEntry } from '@red/domain';
|
||||
import { NestedFilter } from '@common-ui/filtering';
|
||||
import { componentLogChecker } from '@utils/filter-utils';
|
||||
import { INestedFilter, NestedFilter } from '@common-ui/filtering';
|
||||
|
||||
@Injectable()
|
||||
export class ComponentLogFilterService {
|
||||
@ -22,8 +21,23 @@ export class ComponentLogFilterService {
|
||||
{
|
||||
slug: 'componentLogFilters',
|
||||
filters: componentLogFilters,
|
||||
checker: componentLogChecker,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
filterComponents(components: ComponentLogEntry[], filters: INestedFilter[]) {
|
||||
const someFiltersChecked = !!filters.find(f => f.checked);
|
||||
if (!someFiltersChecked) {
|
||||
return components;
|
||||
}
|
||||
|
||||
const checkedFiltersIds = filters.reduce((ids, f) => {
|
||||
if (f.checked) {
|
||||
ids.push(f.id);
|
||||
}
|
||||
return ids;
|
||||
}, []);
|
||||
|
||||
return components.filter(c => checkedFiltersIds.includes(c.name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,6 +76,4 @@ export class EditDictionaryDialogComponent extends IqserDialogComponent<EditDict
|
||||
this._loadingService.stop();
|
||||
this.close(this.form.value as ReturnType);
|
||||
}
|
||||
|
||||
protected readonly iconButtonTypes = IconButtonTypes;
|
||||
}
|
||||
|
||||
@ -4,8 +4,9 @@ import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HttpHeaders } from '@angular/common/http';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { ComponentDetails, ComponentLogEntry, IComponentLogData, IComponentLogEntry, IFile } from '@red/domain';
|
||||
import { ComponentDetails, ComponentLogEntry, Dictionary, IComponentLogData, IComponentLogEntry, IFile } from '@red/domain';
|
||||
import { mapEach } from '@common-ui/utils';
|
||||
import { FilePreviewStateService } from '../../modules/file-preview/services/file-preview-state.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ComponentLogService extends GenericService<void> {
|
||||
@ -34,12 +35,18 @@ export class ComponentLogService extends GenericService<void> {
|
||||
);
|
||||
}
|
||||
|
||||
getComponentLogData(dossierTemplateId: string, dossierId: string, fileId: string): Observable<ComponentLogEntry[]> {
|
||||
getComponentLogData(
|
||||
dossierTemplateId: string,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
dictionaries: Dictionary[],
|
||||
): Observable<ComponentLogEntry[]> {
|
||||
return this.#componentLogRequest(dossierTemplateId, dossierId, fileId).pipe(
|
||||
map(data => data.componentDetails),
|
||||
catchError(() => of({} as ComponentDetails)),
|
||||
map(componentDetails => this.#mapComponentDetails(componentDetails)),
|
||||
mapEach(log => new ComponentLogEntry(log)),
|
||||
map(log => this.#updateDisplayValue(log, dictionaries)),
|
||||
);
|
||||
}
|
||||
|
||||
@ -91,4 +98,17 @@ export class ComponentLogService extends GenericService<void> {
|
||||
#mapComponentDetails(componentDetails: ComponentDetails): IComponentLogEntry[] {
|
||||
return Object.keys(componentDetails).reduce((res, key) => (res.push(componentDetails[key]), res), []);
|
||||
}
|
||||
|
||||
#updateDisplayValue(componentLogs: ComponentLogEntry[], dictionaries: Dictionary[]): ComponentLogEntry[] {
|
||||
for (const componentLog of componentLogs) {
|
||||
for (const componentValue of componentLog.componentValues) {
|
||||
for (const reference of componentValue.entityReferences) {
|
||||
const foundDictionary =
|
||||
dictionaries.find(dict => dict.type === reference.type) ?? ({ label: reference.type } as Dictionary);
|
||||
reference.displayValue = foundDictionary.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
return componentLogs;
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +84,6 @@ export const dossierTemplateChecker = (dw: Dossier, filter: INestedFilter) => dw
|
||||
export const dossierStateChecker = (dw: Dossier, filter: INestedFilter) =>
|
||||
dw.dossierStatusId === (filter.id === 'undefined' ? null : filter.id);
|
||||
|
||||
export const componentLogChecker = (componentLogEntry: ComponentLogEntry, filter: INestedFilter) => componentLogEntry.name === filter.id;
|
||||
|
||||
export const userTypeFilters: { [key in UserType]: (user: User) => boolean } = {
|
||||
INACTIVE: (user: User) => !user.hasAnyRole,
|
||||
REGULAR: (user: User) => user.roles.length === 1 && user.roles[0] === 'RED_USER',
|
||||
|
||||
@ -367,6 +367,7 @@
|
||||
"annotation": {
|
||||
"pending": "(Pending analysis)"
|
||||
},
|
||||
"annotations": "",
|
||||
"archived-dossiers-listing": {
|
||||
"no-data": {
|
||||
"title": "No archived dossiers."
|
||||
@ -2182,6 +2183,13 @@
|
||||
"header": "Resize {type}"
|
||||
}
|
||||
},
|
||||
"revert-value-dialog": {
|
||||
"actions": {
|
||||
"cancel": "",
|
||||
"revert": ""
|
||||
},
|
||||
"title": ""
|
||||
},
|
||||
"roles": {
|
||||
"inactive": "Inaktiv",
|
||||
"manager-admin": "Manager & admin",
|
||||
|
||||
@ -2183,6 +2183,13 @@
|
||||
"header": "Resize {type}"
|
||||
}
|
||||
},
|
||||
"revert-value-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Cancel",
|
||||
"revert": "Revert to original values"
|
||||
},
|
||||
"title": "Revert to the original values?"
|
||||
},
|
||||
"roles": {
|
||||
"inactive": "Inactive",
|
||||
"manager-admin": "Manager & admin",
|
||||
|
||||
@ -367,6 +367,7 @@
|
||||
"annotation": {
|
||||
"pending": "(Pending analysis)"
|
||||
},
|
||||
"annotations": "",
|
||||
"archived-dossiers-listing": {
|
||||
"no-data": {
|
||||
"title": "No archived dossiers."
|
||||
@ -2182,6 +2183,13 @@
|
||||
"header": "Resize {type}"
|
||||
}
|
||||
},
|
||||
"revert-value-dialog": {
|
||||
"actions": {
|
||||
"cancel": "",
|
||||
"revert": ""
|
||||
},
|
||||
"title": ""
|
||||
},
|
||||
"roles": {
|
||||
"inactive": "Inaktiv",
|
||||
"manager-admin": "Manager & admin",
|
||||
|
||||
@ -2183,6 +2183,13 @@
|
||||
"header": "Resize {type}"
|
||||
}
|
||||
},
|
||||
"revert-value-dialog": {
|
||||
"actions": {
|
||||
"cancel": "Cancel",
|
||||
"revert": "Revert to original values"
|
||||
},
|
||||
"title": "Revert to the original values?"
|
||||
},
|
||||
"roles": {
|
||||
"inactive": "Inactive",
|
||||
"manager-admin": "Manager & admin",
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 301ea99abe1be09687cdbe6d0fbae3bec7eefc23
|
||||
Subproject commit 0d85f78d9a42e2ef8de387ec3b86e2857a4cc3e9
|
||||
Loading…
x
Reference in New Issue
Block a user