RED-8748 - WIP on filtering components

This commit is contained in:
Valentin Mihai 2024-04-24 21:48:35 +03:00
parent 1d2485db3c
commit f709edb2ee
14 changed files with 125 additions and 48 deletions

View File

@ -6,7 +6,7 @@
class="mt-6 mr-10"
></redaction-annotation-icon>
<div class="flex-1">
<div [class.flex-1]="!isDocumine">
<div>
<strong>{{ annotation.superTypeLabel | translate }}</strong>
&nbsp;
@ -15,7 +15,7 @@
</strong>
</div>
<div *ngIf="annotation.typeLabel">
<div *ngIf="annotation.typeLabel" [class.type-label]="isDocumine">
<strong>
<span>{{ annotation.descriptor | translate }}</span
>:

View File

@ -3,6 +3,13 @@
position: relative;
font-size: 11px;
line-height: 14px;
.type-label {
width: 130px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.active-icon-marker-container {

View File

@ -3,6 +3,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { MultiSelectService } from '../../services/multi-select.service';
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
import { Roles } from '@users/roles';
import { getConfig } from '@iqser/common-ui';
@Component({
selector: 'redaction-annotation-card',
@ -10,8 +11,9 @@ import { Roles } from '@users/roles';
styleUrls: ['./annotation-card.component.scss'],
})
export class AnnotationCardComponent {
readonly roles = Roles;
readonly annotationTypesTranslations = annotationTypesTranslations;
protected readonly roles = Roles;
protected readonly annotationTypesTranslations = annotationTypesTranslations;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
@Input() annotation: AnnotationWrapper;
@Input() isSelected = false;

View File

@ -8,6 +8,7 @@
(click)="annotationClicked(annotation.item, $event)"
[annotation]="annotation"
[id]="'annotation-' + annotation.item.id"
[class.documine-wrapper]="isDocumine"
></redaction-annotation-wrapper>
</ng-container>

View File

@ -10,7 +10,8 @@
@include common-mixins.scroll-bar;
}
&.has-scrollbar:hover redaction-annotation-wrapper::ng-deep {
&.has-scrollbar:hover redaction-annotation-wrapper::ng-deep,
&::ng-deep.documine-wrapper {
.annotation {
padding-right: 5px;
}

View File

@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, computed, ElementRef, EventEmitter, Input, Output } from '@angular/core';
import { HasScrollbarDirective } from '@iqser/common-ui';
import { getConfig, HasScrollbarDirective } from '@iqser/common-ui';
import { FilterService } from '@iqser/common-ui/lib/filtering';
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -26,6 +26,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
}
return [] as EarmarkGroup[];
});
protected readonly isDocumine = getConfig().IS_DOCUMINE;
constructor(
protected readonly _elementRef: ElementRef,

View File

@ -11,10 +11,9 @@
(action)="edit()"
*ngIf="canEdit"
[tooltip]="'component-management.actions.edit' | translate"
class="ml-2"
icon="iqser:edit"
></iqser-circle-button>
<div class="changes-dot"></div>
<div class="changes-dot" *ngIf="entry.componentValues[0].value !== entry.componentValues[0].originalValue && canEdit"></div>
</div>
</div>
<mat-icon *ngIf="!editing" class="arrow-right" svgIcon="red:arrow-right"></mat-icon>
@ -22,41 +21,41 @@
<ng-template #editValue>
<form [formGroup]="form" (submit)="save()">
<ng-container *ngFor="let componentValue of entry.componentValues; let index = index">
<div class="editing-value" *ngIf="form.get(index.toString())">
<ng-container *ngFor="let control of form.controls | keyvalue">
<div class="editing-value">
<mat-icon class="draggable" svgIcon="red:draggable-dots"></mat-icon>
<div class="iqser-input-group w-400">
<textarea [formControlName]="index" rows="1" type="text"></textarea>
<div class="iqser-input-group w-full">
<textarea [formControlName]="control.key" rows="1" type="text"></textarea>
</div>
<iqser-circle-button
(action)="removeValue(index)"
(action)="removeValue(control.key)"
[tooltip]="'component-management.actions.delete' | translate"
class="ml-2"
class="remove-value"
icon="iqser:trash"
></iqser-circle-button>
</div>
</ng-container>
<div class="editing-actions">
<iqser-icon-button
[disabled]="disabled"
(action)="save()"
[label]="'component-management.actions.save' | translate"
[type]="iconButtonTypes.primary"
></iqser-icon-button>
<div (click)="deselect($event)" class="all-caps-label cancel" translate="component-management.actions.cancel"></div>
<!-- *ngIf="entry.componentValues[0].value !== entry.componentValues[0].originalValue && canEdit"-->
<div class="flex right">
<iqser-circle-button
*ngIf="entry.componentValues[0].value !== entry.componentValues[0].originalValue && canEdit"
(action)="undo(entry.originalKey)"
[showDot]="true"
[tooltip]="'component-management.actions.undo' | translate"
class="ml-2"
class="undo-value"
icon="red:undo"
></iqser-circle-button>
<iqser-circle-button
(action)="add()"
[tooltip]="'component-management.actions.add' | translate"
class="ml-2"
class="add-value"
icon="iqser:plus"
></iqser-circle-button>
</div>

View File

@ -90,18 +90,26 @@
.editing-value {
display: flex;
align-items: center;
margin: 10px 0 10px 22px;
.iqser-input-group {
margin-top: 0;
textarea {
resize: none;
textarea::-webkit-resizer {
display: none;
}
textarea::-moz-resizer {
display: none;
}
}
iqser-circle-button {
margin-top: 3px;
}
.draggable {
margin-top: 7px;
cursor: grab;
}
}
@ -111,11 +119,30 @@
flex-direction: row;
align-items: center;
gap: 12px;
margin: 20px 10px 10px 22px;
margin: 30px 10px 10px 22px;
.right {
margin-left: auto;
gap: 10px;
}
}
}
}
::ng-deep .add-value {
mat-icon {
transform: scale(2);
}
}
::ng-deep .undo-value {
mat-icon {
transform: scale(1.3);
}
}
::ng-deep .remove-value {
mat-icon {
transform: scale(1.2);
}
}

View File

@ -1,36 +1,29 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ComponentLogEntry } from '@red/domain';
import { FormBuilder, FormControl, UntypedFormGroup } from '@angular/forms';
import { IconButtonTypes } from '@iqser/common-ui';
import { BaseFormComponent } from '@iqser/common-ui';
@Component({
selector: 'redaction-editable-structured-component-value [entry] [canEdit]',
templateUrl: './editable-structured-component-value.component.html',
styleUrls: ['/editable-structured-component-value.component.scss'],
})
export class EditableStructuredComponentValueComponent implements OnInit {
export class EditableStructuredComponentValueComponent extends BaseFormComponent implements OnInit {
@Input() entry: ComponentLogEntry;
@Input() canEdit: boolean;
@Output() readonly deselectLast = new EventEmitter();
protected readonly iconButtonTypes = IconButtonTypes;
form: UntypedFormGroup;
selected = false;
editing = false;
constructor(private readonly _formBuilder: FormBuilder) {}
constructor(private readonly _formBuilder: FormBuilder) {
super();
}
ngOnInit() {
this.form = this.#getForm();
}
#getForm() {
const form = this._formBuilder.group({});
this.entry.componentValues.forEach((value, index) => {
form.addControl(index.toString(), this._formBuilder.control(value.value ?? value.originalValue));
});
return form;
this.initialFormValue = this.form.getRawValue();
}
select() {
@ -56,23 +49,26 @@ export class EditableStructuredComponentValueComponent implements OnInit {
this.editing = false;
}
removeValue(key: number) {
this.form.removeControl(key.toString());
removeValue(key: string) {
this.form.removeControl(key);
}
save() {}
async save() {
const value = this.form.getRawValue();
}
undo(originalKey: string) {}
add() {
const key = (Object.keys(this.form.controls).length + 1).toString();
const key = Object.keys(this.form.controls).length.toString();
this.form.addControl(key, new FormControl(''));
this.entry.componentValues.push({
value: '',
originalValue: '',
valueDescription: '',
componentRuleId: '',
entityReferences: [],
}
#getForm() {
const form = this._formBuilder.group({});
this.entry.componentValues.forEach((value, index) => {
form.addControl(index.toString(), this._formBuilder.control(value.value ?? value.originalValue));
});
return form;
}
}

View File

@ -1,6 +1,6 @@
<div class="components-header">
<span [translate]="'component-management.components'"></span>
<iqser-popup-filter [primaryFiltersSlug]="'primaryFilters'" [secondaryFiltersSlug]="'secondaryFilters'"></iqser-popup-filter>
<iqser-popup-filter [primaryFiltersSlug]="'componentLogFilters'"></iqser-popup-filter>
</div>
<div *ngIf="componentLogData() as componentLogEntries" class="components-container">

View File

@ -1,4 +1,4 @@
import { Component, Input, QueryList, signal, ViewChildren } from '@angular/core';
import { Component, Input, 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';
@ -7,6 +7,8 @@ import { UserPreferenceService } from '@users/user-preference.service';
import { firstValueFrom } 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;
@ -31,6 +33,8 @@ export class StructuredComponentManagementComponent {
private readonly _componentLogService: ComponentLogService,
private readonly _filesMapService: FilesMapService,
private readonly _loadingService: LoadingService,
private readonly _componentLogFilterService: ComponentLogFilterService,
private readonly _filterService: FilterService,
readonly userPreferences: UserPreferenceService,
) {}
@ -100,6 +104,7 @@ export class StructuredComponentManagementComponent {
const componentLogData = await firstValueFrom(
this._componentLogService.getComponentLogData(this.file.dossierTemplateId, this.file.dossierId, this.file.fileId),
);
this.#computeFilters(componentLogData);
this.#updateDisplayValue(componentLogData);
this.componentLogData.set(componentLogData);
this._loadingService.stop();
@ -120,4 +125,9 @@ export class StructuredComponentManagementComponent {
}
}
}
#computeFilters(componentLogs: ComponentLogEntry[]) {
const filterGroups = this._componentLogFilterService.filterGroups(componentLogs);
this._filterService.addFilterGroups(filterGroups);
}
}

View File

@ -16,6 +16,7 @@ import { PdfProxyService } from './services/pdf-proxy.service';
import { SkippedService } from './services/skipped.service';
import { StampService } from './services/stamp.service';
import { ViewModeService } from './services/view-mode.service';
import { ComponentLogFilterService } from './services/component-log-filter.service';
export const filePreviewScreenProviders = [
FilterService,
@ -38,4 +39,5 @@ export const filePreviewScreenProviders = [
SearchService,
StampService,
PdfProxyService,
ComponentLogFilterService,
];

View File

@ -0,0 +1,29 @@
import { Injectable } from '@angular/core';
import { ComponentLogEntry } from '@red/domain';
import { NestedFilter } from '@common-ui/filtering';
import { componentLogChecker } from '@utils/filter-utils';
@Injectable()
export class ComponentLogFilterService {
filterGroups(entities: ComponentLogEntry[]) {
const allDistinctComponentLogs = new Set<string>();
entities?.forEach(entry => allDistinctComponentLogs.add(entry.name));
const componentLogFilters = [...allDistinctComponentLogs].map(
id =>
new NestedFilter({
id: id,
label: id,
}),
);
return [
{
slug: 'componentLogFilters',
filters: componentLogFilters,
checker: componentLogChecker,
},
];
}
}

View File

@ -1,5 +1,5 @@
import { handleCheckedValue, INestedFilter } from '@iqser/common-ui/lib/filtering';
import { Dossier, File, User, UserType } from '@red/domain';
import { ComponentLogEntry, Dossier, File, User, UserType } from '@red/domain';
export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) {
const newFiltersDelta = {};
@ -84,6 +84,8 @@ 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',