Pull request #290: RED-2211
Merge in RED/ui from RED-2211 to master * commit '01233562870907faf875eee573219961e940bc5e': add engines property and show relevant messages in popover wip redaction sources add name pipe move annotations list to a separate component
This commit is contained in:
commit
7429708518
@ -57,11 +57,10 @@ export class AnnotationWrapper {
|
||||
|
||||
isChangeLogEntry?: boolean;
|
||||
changeLogType?: 'ADDED' | 'REMOVED';
|
||||
engines?: string[];
|
||||
|
||||
private _origin: RedactionLogEntryWrapper;
|
||||
|
||||
constructor() {}
|
||||
|
||||
get isChangeLogRemoved() {
|
||||
return this.changeLogType === 'REMOVED';
|
||||
}
|
||||
@ -220,6 +219,7 @@ export class AnnotationWrapper {
|
||||
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
|
||||
annotationWrapper.comments = redactionLogEntry.comments || [];
|
||||
annotationWrapper.manual = redactionLogEntry.manual;
|
||||
annotationWrapper.engines = redactionLogEntry.engines;
|
||||
|
||||
this._createContent(annotationWrapper, redactionLogEntry);
|
||||
this._setSuperType(annotationWrapper, redactionLogEntry);
|
||||
|
||||
@ -35,4 +35,5 @@ export interface RedactionLogEntryWrapper {
|
||||
|
||||
recategorizationType?: string;
|
||||
legalBasisChangeValue?: string;
|
||||
engines?: string[];
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div [translateParams]="{ userName: userName }" [translate]="'reset-password-dialog.header'" class="dialog-header heading-l"></div>
|
||||
<div [translateParams]="{ userName: user | name }" [translate]="'reset-password-dialog.header'" class="dialog-header heading-l"></div>
|
||||
|
||||
<form (submit)="save()" [formGroup]="passwordForm">
|
||||
<div class="dialog-content">
|
||||
|
||||
@ -23,10 +23,6 @@ export class ResetPasswordComponent {
|
||||
private readonly _loadingService: LoadingService
|
||||
) {}
|
||||
|
||||
get userName() {
|
||||
return this._userService.getNameForId(this.user.id);
|
||||
}
|
||||
|
||||
async save() {
|
||||
this._loadingService.start();
|
||||
await this._userControllerService
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<div *ngFor="let comment of annotation.comments" class="comment">
|
||||
<div class="comment-details-wrapper">
|
||||
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label">
|
||||
<b> {{ getOwnerName(comment) }} </b>
|
||||
<strong> {{ comment.user | name }} </strong>
|
||||
{{ comment.date | date: 'sophisticatedDate' }}
|
||||
</div>
|
||||
|
||||
<div class="comment-actions">
|
||||
<iqser-circle-button
|
||||
(action)="deleteComment(comment)"
|
||||
@ -15,6 +16,7 @@
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>{{ comment.text }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -52,8 +52,4 @@ export class CommentsComponent {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getOwnerName(comment: Comment): string {
|
||||
return this._userService.getNameForId(comment.user);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
<ng-container *ngIf="hasEnginesToShow">
|
||||
<div cdkOverlayOrigin #trigger="cdkOverlayOrigin" class="chip" (mouseover)="isPopoverOpen = true" (mouseout)="isPopoverOpen = false">
|
||||
<ng-container *ngFor="let engine of engines">
|
||||
<mat-icon *ngIf="engine.show" [svgIcon]="engine.icon"></mat-icon>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template
|
||||
cdkConnectedOverlay
|
||||
[cdkConnectedOverlayOffsetY]="-8"
|
||||
[cdkConnectedOverlayOrigin]="trigger"
|
||||
[cdkConnectedOverlayOpen]="isPopoverOpen"
|
||||
>
|
||||
<div class="popover">
|
||||
<ng-container *ngFor="let engine of engines">
|
||||
<div *ngIf="engine.show" class="flex-align-items-center">
|
||||
<mat-icon [svgIcon]="engine.icon"></mat-icon>
|
||||
<span>{{ engine.description }}</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
@ -0,0 +1,51 @@
|
||||
@use 'variables';
|
||||
|
||||
.popover {
|
||||
width: 260px;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
background-color: variables.$grey-1;
|
||||
color: variables.$white;
|
||||
|
||||
mat-icon {
|
||||
color: variables.$white;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 8px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.chip {
|
||||
height: 24px;
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-6;
|
||||
border-radius: 12px;
|
||||
|
||||
mat-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
opacity: 50%;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { OnChange } from '@iqser/common-ui';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
interface Engine {
|
||||
readonly icon: string;
|
||||
readonly description: string;
|
||||
readonly show: boolean;
|
||||
}
|
||||
|
||||
type Engines = readonly Engine[];
|
||||
|
||||
const Engines = {
|
||||
DICTIONARY: 'DICTIONARY',
|
||||
NER: 'NER',
|
||||
RULE: 'RULE'
|
||||
} as const;
|
||||
|
||||
type EngineName = keyof typeof Engines;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-source',
|
||||
templateUrl: './annotation-source.component.html',
|
||||
styleUrls: ['./annotation-source.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AnnotationSourceComponent {
|
||||
@Input()
|
||||
@OnChange<AnnotationWrapper, AnnotationSourceComponent>('updateEngines')
|
||||
annotation: AnnotationWrapper;
|
||||
|
||||
isPopoverOpen = false;
|
||||
engines: Engines;
|
||||
|
||||
constructor(private readonly _translateService: TranslateService) {}
|
||||
|
||||
get hasEnginesToShow(): boolean {
|
||||
return this.engines.length && this.engines.some(source => source.show);
|
||||
}
|
||||
|
||||
updateEngines(): void {
|
||||
this.engines = [
|
||||
{
|
||||
icon: 'red:dictionary',
|
||||
description: this._translateService.instant('annotation-engines.dictionary'),
|
||||
show: this._isBasedOn(Engines.DICTIONARY)
|
||||
},
|
||||
{
|
||||
icon: 'red:ai',
|
||||
description: this._translateService.instant('annotation-engines.ner'),
|
||||
show: this._isBasedOn(Engines.NER)
|
||||
},
|
||||
{
|
||||
icon: 'red:rule',
|
||||
description: this._translateService.instant('annotation-engines.rule', { rule: this.annotation.legalBasisValue }),
|
||||
show: this._isBasedOn(Engines.RULE)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private _isBasedOn(engineName: EngineName) {
|
||||
return !!this.annotation.engines?.includes(engineName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
<div
|
||||
(click)="annotationClicked(annotation, $event)"
|
||||
*ngFor="let annotation of annotations"
|
||||
[attr.annotation-id]="annotation.id"
|
||||
[attr.annotation-page]="activeViewerPage"
|
||||
[class.active]="isSelected(annotation.annotationId)"
|
||||
[class.multi-select-active]="multiSelectActive"
|
||||
class="annotation-wrapper"
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
|
||||
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
||||
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
|
||||
<redaction-type-annotation-icon [annotation]="annotation | log"></redaction-type-annotation-icon>
|
||||
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation?.type !== 'manual'">
|
||||
<strong>
|
||||
<span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.type | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.shortContent && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-icon-marker-container">
|
||||
<iqser-round-checkbox
|
||||
*ngIf="multiSelectActive && isSelected(annotation.annotationId)"
|
||||
[active]="true"
|
||||
></iqser-round-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-wrapper">
|
||||
<div
|
||||
(click)="comments.toggleExpandComments($event)"
|
||||
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
|
||||
class="comments-counter"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
{{ annotation.comments.length }}
|
||||
</div>
|
||||
|
||||
<div *ngIf="!multiSelectActive" class="actions">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ annotation: annotation }"
|
||||
[ngTemplateOutlet]="annotationActionsTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<redaction-comments #comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
|
||||
<redaction-annotation-source [annotation]="annotation"></redaction-annotation-source>
|
||||
</div>
|
||||
@ -0,0 +1,96 @@
|
||||
@use 'variables';
|
||||
|
||||
:host {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid variables.$separator;
|
||||
|
||||
.active-bar-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.active-icon-marker-container {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.lower-height) .active-bar-marker {
|
||||
background-color: variables.$primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 16px 8px 10px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: variables.$grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actions-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
min-height: 34px;
|
||||
padding-left: 18px;
|
||||
|
||||
.comments-counter {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
transition: background-color 0.2s;
|
||||
line-height: 13px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-8;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-annotation-source {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { IqserEventTarget } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotations-list',
|
||||
templateUrl: './annotations-list.component.html',
|
||||
styleUrls: ['./annotations-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class AnnotationsListComponent {
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Input() annotationActionsTemplate: TemplateRef<unknown>;
|
||||
@Input() multiSelectActive = false;
|
||||
@Input() activeViewerPage: number;
|
||||
@Input() canMultiSelect = true;
|
||||
|
||||
@Output() readonly multiSelectActiveChange = new EventEmitter<boolean>();
|
||||
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
|
||||
@Output() readonly selectAnnotations = new EventEmitter<
|
||||
AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }
|
||||
>();
|
||||
@Output() readonly deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
|
||||
if (($event.target as IqserEventTarget).localName === 'input') {
|
||||
return;
|
||||
}
|
||||
this.pagesPanelActive.emit(false);
|
||||
if (this.isSelected(annotation.annotationId)) {
|
||||
this.deselectAnnotations.emit([annotation]);
|
||||
} else {
|
||||
if (this.canMultiSelect && ($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
this.multiSelectActiveChange.emit(true);
|
||||
}
|
||||
this.selectAnnotations.emit({
|
||||
annotations: [annotation],
|
||||
multiSelect: this.multiSelectActive
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isSelected(annotationId: string): boolean {
|
||||
return !!this.selectedAnnotations?.find(a => a?.annotationId === annotationId);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,3 @@
|
||||
<!-- This is a hack to subscribe to an observable using async pipe instead of component class -->
|
||||
<ng-container *ngIf="displayedAnnotations$ | async"></ng-container>
|
||||
|
||||
<div *ngIf="!excludePages" class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<div
|
||||
@ -150,8 +147,7 @@
|
||||
(click)="actionPerformed.emit('view-exclude-pages')"
|
||||
class="with-underline"
|
||||
translate="file-preview.excluded-from-redaction"
|
||||
>
|
||||
</a
|
||||
></a
|
||||
>.
|
||||
</ng-container>
|
||||
</iqser-empty-state>
|
||||
@ -174,62 +170,17 @@
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation, $event)"
|
||||
*ngFor="let annotation of displayedAnnotations.get(activeViewerPage)"
|
||||
[attr.annotation-id]="annotation.id"
|
||||
[attr.annotation-page]="activeViewerPage"
|
||||
[class.active]="isSelected(annotation)"
|
||||
[class.multi-select-active]="multiSelectActive"
|
||||
class="annotation-wrapper"
|
||||
>
|
||||
<div class="active-bar-marker"></div>
|
||||
<div [class.removed]="annotation.isChangeLogRemoved" class="annotation">
|
||||
<div [matTooltip]="annotation.content" class="details" matTooltipPosition="above">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation?.type !== 'manual'">
|
||||
<strong>
|
||||
<span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.type | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.shortContent && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-icon-marker-container">
|
||||
<iqser-round-checkbox
|
||||
*ngIf="multiSelectActive && isSelected(annotation)"
|
||||
[active]="true"
|
||||
></iqser-round-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-wrapper">
|
||||
<div
|
||||
(click)="toggleExpandComments(annotation, $event)"
|
||||
[matTooltip]="'comments.comments' | translate: { count: annotation.comments?.length }"
|
||||
class="comments-counter"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||
{{ annotation.comments.length }}
|
||||
</div>
|
||||
<div *ngIf="!multiSelectActive" class="actions">
|
||||
<ng-container
|
||||
[ngTemplateOutletContext]="{ annotation: annotation }"
|
||||
[ngTemplateOutlet]="annotationActionsTemplate"
|
||||
></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-annotations-list
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[(multiSelectActive)]="multiSelectActive"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
(pagesPanelActive)="pagesPanelActive = $event"
|
||||
(selectAnnotations)="selectAnnotations.emit($event)"
|
||||
(deselectAnnotations)="deselectAnnotations.emit($event)"
|
||||
></redaction-annotations-list>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@ -157,96 +157,12 @@
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid variables.$separator;
|
||||
|
||||
.active-bar-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.active-icon-marker-container {
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
&:not(.lower-height) .active-bar-marker {
|
||||
background-color: variables.$primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 16px 8px 10px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: variables.$grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.actions-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
min-height: 34px;
|
||||
padding-left: 18px;
|
||||
|
||||
.comments-counter {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
transition: background-color 0.2s;
|
||||
line-height: 13px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
|
||||
mat-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: variables.$grey-8;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover .annotation-wrapper .annotation {
|
||||
&.has-scrollbar:hover::ng-deep .annotation-wrapper .annotation {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,10 @@
|
||||
import {
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Input,
|
||||
Output,
|
||||
QueryList,
|
||||
TemplateRef,
|
||||
ViewChild,
|
||||
ViewChildren
|
||||
} from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, IqserEventTarget, NestedFilter } from '@iqser/common-ui';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { CommentsComponent } from '../comments/comments.component';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
|
||||
@ -56,7 +43,6 @@ export class FileWorkloadComponent {
|
||||
@Output() readonly actionPerformed = new EventEmitter<string>();
|
||||
displayedPages: number[] = [];
|
||||
pagesPanelActive = true;
|
||||
@ViewChildren(CommentsComponent) readonly annotationCommentsComponents: QueryList<CommentsComponent>;
|
||||
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
|
||||
|
||||
@ -118,15 +104,6 @@ export class FileWorkloadComponent {
|
||||
}
|
||||
}
|
||||
|
||||
isSelected(annotation: AnnotationWrapper) {
|
||||
return this.selectedAnnotations?.find(a => a?.id === annotation.id);
|
||||
}
|
||||
|
||||
toggleExpandComments(annotation: AnnotationWrapper, $event: MouseEvent) {
|
||||
$event.stopPropagation();
|
||||
this.annotationCommentsComponents.find(c => c.annotation === annotation).toggleExpandComments();
|
||||
}
|
||||
|
||||
logAnnotation(annotation: AnnotationWrapper) {
|
||||
console.log(annotation);
|
||||
}
|
||||
@ -156,25 +133,6 @@ export class FileWorkloadComponent {
|
||||
return this.displayedAnnotations;
|
||||
}
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
|
||||
if (($event.target as IqserEventTarget).localName === 'input') {
|
||||
return;
|
||||
}
|
||||
this.pagesPanelActive = false;
|
||||
this.logAnnotation(annotation);
|
||||
if (this.isSelected(annotation)) {
|
||||
this.deselectAnnotations.emit([annotation]);
|
||||
} else {
|
||||
if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
|
||||
this.multiSelectActive = true;
|
||||
}
|
||||
this.selectAnnotations.emit({
|
||||
annotations: [annotation],
|
||||
multiSelect: this.multiSelectActive
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent): void {
|
||||
if (
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<mat-label>{{ 'assign-dossier-owner.dialog.single-user' | translate }}</mat-label>
|
||||
<mat-select formControlName="owner">
|
||||
<mat-option *ngFor="let userId of ownersSelectOptions" [value]="userId">
|
||||
{{ userService.getNameForId(userId) }}
|
||||
{{ userId | name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<mat-label>{{ 'assign-owner.dialog.label' | translate: { type: data.mode } }}</mat-label>
|
||||
<mat-select formControlName="singleUser">
|
||||
<mat-option *ngFor="let userId of singleUsersSelectOptions" [value]="userId">
|
||||
{{ userService.getNameForId(userId) }}
|
||||
{{ userId | name }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@ -49,6 +49,9 @@ import { DossiersService } from './services/dossiers.service';
|
||||
import { DossierDetailsStatsComponent } from './components/dossier-details-stats/dossier-details-stats.component';
|
||||
import { SearchScreenComponent } from './screens/search-screen/search-screen.component';
|
||||
import { EditDossierDeletedDocumentsComponent } from './dialogs/edit-dossier-dialog/deleted-documents/edit-dossier-deleted-documents.component';
|
||||
import { AnnotationsListComponent } from './components/file-workload/components/annotations-list/annotations-list.component';
|
||||
import { AnnotationSourceComponent } from './components/file-workload/components/annotation-source/annotation-source.component';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
|
||||
const screens = [DossierListingScreenComponent, DossierOverviewScreenComponent, FilePreviewScreenComponent, SearchScreenComponent];
|
||||
|
||||
@ -89,6 +92,8 @@ const components = [
|
||||
PageExclusionComponent,
|
||||
DossierDetailsStatsComponent,
|
||||
EditDossierDeletedDocumentsComponent,
|
||||
AnnotationsListComponent,
|
||||
AnnotationSourceComponent,
|
||||
|
||||
...screens,
|
||||
...dialogs
|
||||
@ -109,6 +114,6 @@ const services = [
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
providers: [...services],
|
||||
imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule]
|
||||
imports: [CommonModule, SharedModule, FileUploadDownloadModule, DossiersRoutingModule, OverlayModule]
|
||||
})
|
||||
export class DossiersModule {}
|
||||
|
||||
@ -16,11 +16,12 @@ export class AnnotationDrawService {
|
||||
) {}
|
||||
|
||||
drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[], hideSkipped = false, compareMode = false) {
|
||||
const annotations = [];
|
||||
annotationWrappers.forEach(annotation => {
|
||||
annotations.push(this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode));
|
||||
});
|
||||
|
||||
if (!activeViewer) {
|
||||
return;
|
||||
}
|
||||
const annotations = annotationWrappers.map(annotation =>
|
||||
this.computeAnnotation(activeViewer, annotation, hideSkipped, compareMode)
|
||||
);
|
||||
const annotationManager = activeViewer.Core.annotationManager;
|
||||
annotationManager.addAnnotations(annotations, { imported: true });
|
||||
annotationManager.drawAnnotationsFromList(annotations);
|
||||
|
||||
@ -12,6 +12,7 @@ export class IconsModule {
|
||||
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
|
||||
const icons = [
|
||||
'add',
|
||||
'ai',
|
||||
'analyse',
|
||||
'approved',
|
||||
'arrow-right',
|
||||
@ -62,6 +63,7 @@ export class IconsModule {
|
||||
'reason',
|
||||
'remove-from-dict',
|
||||
'report',
|
||||
'rule',
|
||||
'secret',
|
||||
'status',
|
||||
'status-collapse',
|
||||
|
||||
17
apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts
Normal file
17
apps/red-ui/src/app/modules/shared/pipes/name.pipe.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { UserService, UserWrapper } from '@services/user.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'name'
|
||||
})
|
||||
export class NamePipe implements PipeTransform {
|
||||
constructor(private readonly _userService: UserService, private readonly _translateService: TranslateService) {}
|
||||
|
||||
transform(value: UserWrapper | string): string {
|
||||
if (typeof value === 'string') {
|
||||
return this._userService.getNameForId(value) || this._translateService.instant('unknown');
|
||||
}
|
||||
return value.name;
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ import { AssignUserDropdownComponent } from './components/assign-user-dropdown/a
|
||||
import { PageHeaderComponent } from './components/page-header/page-header.component';
|
||||
import { DatePipe } from '@shared/pipes/date.pipe';
|
||||
import { LongPressDirective } from '@shared/directives/long-press.directive';
|
||||
import { NamePipe } from '@shared/pipes/name.pipe';
|
||||
|
||||
const buttons = [FileDownloadBtnComponent, UserButtonComponent];
|
||||
|
||||
@ -45,7 +46,7 @@ const components = [
|
||||
...buttons
|
||||
];
|
||||
|
||||
const utils = [DatePipe, NavigateLastDossiersScreenDirective, LongPressDirective];
|
||||
const utils = [DatePipe, NamePipe, NavigateLastDossiersScreenDirective, LongPressDirective];
|
||||
|
||||
const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, ReactiveFormsModule, CommonUiModule];
|
||||
|
||||
@ -68,4 +69,5 @@ const modules = [MatConfigModule, ScrollingModule, IconsModule, FormsModule, Rea
|
||||
}
|
||||
]
|
||||
})
|
||||
export class SharedModule {}
|
||||
export class SharedModule {
|
||||
}
|
||||
|
||||
@ -106,8 +106,7 @@ export class UserService {
|
||||
}
|
||||
|
||||
getNameForId(userId: string): string | undefined {
|
||||
const user = this.getUserById(userId);
|
||||
return user ? user.name : undefined;
|
||||
return this.getUserById(userId)?.name;
|
||||
}
|
||||
|
||||
isManager(user: UserWrapper = this._currentUser): boolean {
|
||||
|
||||
@ -1379,5 +1379,10 @@
|
||||
"text-placeholder": "Text eingeben"
|
||||
},
|
||||
"title": "Wasserzeichen"
|
||||
},
|
||||
"annotation-engines": {
|
||||
"dictionary": "",
|
||||
"ner": "",
|
||||
"rule": ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -1521,5 +1521,10 @@
|
||||
},
|
||||
"title": "Watermark"
|
||||
},
|
||||
"yesterday": "Yesterday"
|
||||
"yesterday": "Yesterday",
|
||||
"annotation-engines": {
|
||||
"dictionary": "Redaction based on dictionary",
|
||||
"ner": "Redaction based on AI",
|
||||
"rule": "Redaction based on rule {rule}"
|
||||
}
|
||||
}
|
||||
|
||||
12
apps/red-ui/src/assets/icons/general/ai.svg
Normal file
12
apps/red-ui/src/assets/icons/general/ai.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>ai</title>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="ai_3" fill="currentColor" fill-rule="nonzero">
|
||||
<path
|
||||
d="M20.4612365,85.4425908 L20.4612365,79.2600589 L15.063788,78.3277723 L19.9214917,65.1776251 L48.5279686,65.1776251 L53.2384691,78.3277723 L47.8410206,79.2600589 L47.8410206,85.4425908 L68.3513248,85.4425908 L68.3513248,79.2600589 L63.1010795,78.5240432 L38.5672228,14 L30.2747792,14 L5.29931305,78.5240432 L1.42108547e-14,79.2600589 L1.42108547e-14,85.4425908 L20.4612365,85.4425908 Z M45.5839058,57.0814524 L22.9146222,57.0814524 L34.2001963,26.4141315 L34.4946026,26.4141315 L45.5839058,57.0814524 Z M98,85.4425908 L98,79.2600589 L90.0019627,77.8861629 L90.0019627,21.6054956 L98,20.2315996 L98,14 L72.3375859,14 L72.3375859,20.2315996 L80.3356232,21.6054956 L80.3356232,77.8861629 L72.3375859,79.2600589 L72.3375859,85.4425908 L98,85.4425908 Z"
|
||||
id="AI"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
12
apps/red-ui/src/assets/icons/general/rule.svg
Normal file
12
apps/red-ui/src/assets/icons/general/rule.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>rule</title>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="rule" fill="currentColor" fill-rule="nonzero">
|
||||
<path
|
||||
d="M53.07405,4 L62.93805,5.641 L46.92795,95.8114 L37.06395,94.1704 L53.07405,4 Z M72,21.9047 L100,49.9047 L72,77.9047 L65,70.9047 L86,49.9047 L65,28.9047 L72,21.9047 Z M28,21.9047 L35,28.9047 L14,49.9047 L35,70.9047 L28,77.9047 L0,49.9047 L28,21.9047 Z"
|
||||
id="Combined-Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 677 B |
@ -1 +1 @@
|
||||
Subproject commit a16d1db3ab938dccc2c9d82c5e3d283bb857f46c
|
||||
Subproject commit b55e3bf0ddbda8e8459cb0ae23c8abd5fff46a18
|
||||
@ -40,6 +40,7 @@ export interface RedactionLogEntry {
|
||||
type?: string;
|
||||
value?: string;
|
||||
legalBasisChangeValue?: string;
|
||||
engines?: string[];
|
||||
}
|
||||
|
||||
export namespace RedactionLogEntry {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user