Merge branch 'master' into VM/RED-8748
This commit is contained in:
commit
c024d06806
@ -28,7 +28,7 @@ export const canRemoveFromDictionary = (annotation: AnnotationWrapper, autoAnaly
|
||||
(annotation.isRedacted || annotation.isSkipped || annotation.isHint || (annotation.isIgnoredHint && !annotation.isRuleBased)) &&
|
||||
(autoAnalysisDisabled || !annotation.pending) &&
|
||||
!annotation.hasBeenResizedLocally &&
|
||||
(!annotation.hasBeenForcedHint || !annotation.hasBeenForcedRedaction);
|
||||
!(annotation.hasBeenForcedHint || annotation.hasBeenForcedRedaction);
|
||||
|
||||
export const canRemoveRedaction = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
||||
(!annotation.isIgnoredHint || !annotation.isRuleBased) &&
|
||||
@ -58,6 +58,12 @@ export const canResizeAnnotation = (
|
||||
(!annotation.isHint && hasDictionary && annotation.isRuleBased))) ||
|
||||
annotation.isRecommendation;
|
||||
|
||||
export const canResizeInDictionary = (annotation: AnnotationWrapper, permissions: AnnotationPermissions) =>
|
||||
permissions.canResizeAnnotation &&
|
||||
annotation.isModifyDictionary &&
|
||||
!annotation.hasBeenResizedLocally &&
|
||||
!(annotation.hasBeenForcedHint || annotation.hasBeenForcedRedaction);
|
||||
|
||||
export const canEditAnnotation = (annotation: AnnotationWrapper) => (annotation.isRedacted || annotation.isSkipped) && !annotation.isImage;
|
||||
|
||||
export const canEditHint = (annotation: AnnotationWrapper) =>
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
canRemoveOnlyHere,
|
||||
canRemoveRedaction,
|
||||
canResizeAnnotation,
|
||||
canResizeInDictionary,
|
||||
canUndo,
|
||||
} from './annotation-permissions.utils';
|
||||
import { AnnotationWrapper } from './annotation.wrapper';
|
||||
@ -30,6 +31,7 @@ export class AnnotationPermissions {
|
||||
canForceRedaction = true;
|
||||
canChangeLegalBasis = true;
|
||||
canResizeAnnotation = true;
|
||||
canResizeInDictionary = true;
|
||||
canRecategorizeAnnotation = true;
|
||||
canForceHint = true;
|
||||
canEditAnnotations = true;
|
||||
@ -69,6 +71,7 @@ export class AnnotationPermissions {
|
||||
autoAnalysisDisabled,
|
||||
annotationEntity?.hasDictionary,
|
||||
);
|
||||
permissions.canResizeInDictionary = canResizeInDictionary(annotation, permissions);
|
||||
permissions.canEditAnnotations = canEditAnnotation(annotation);
|
||||
permissions.canEditHints = canEditHint(annotation);
|
||||
permissions.canEditImages = canEditImage(annotation);
|
||||
@ -80,6 +83,7 @@ export class AnnotationPermissions {
|
||||
static reduce(permissions: AnnotationPermissions[]): AnnotationPermissions {
|
||||
const result = new AnnotationPermissions();
|
||||
result.canResizeAnnotation = permissions.length === 1 && permissions[0].canResizeAnnotation;
|
||||
result.canResizeInDictionary = permissions.length === 1 && permissions[0].canResizeInDictionary;
|
||||
result.canChangeLegalBasis = permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true);
|
||||
result.canRecategorizeAnnotation = permissions.reduce((acc, next) => acc && next.canRecategorizeAnnotation, true);
|
||||
result.canRemoveFromDictionary = permissions.reduce((acc, next) => acc && next.canRemoveFromDictionary, true);
|
||||
|
||||
@ -23,9 +23,3 @@ form {
|
||||
font-size: 16px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
background: var(--iqser-light);
|
||||
border: 1px solid var(--iqser-grey-1);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { LicenseService } from '@services/license.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ChartDataset } from 'chart.js';
|
||||
import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
|
||||
import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { size } from '@iqser/common-ui/lib/utils';
|
||||
@ -48,7 +48,7 @@ export class LicenseAnalysisCapacityUsageComponent {
|
||||
},
|
||||
|
||||
{
|
||||
data: monthlyData.map(
|
||||
data: getDataUntilCurrentMonth(monthlyData).map(
|
||||
(month, monthIndex) =>
|
||||
month.analysedFilesBytes + monthlyData.slice(0, monthIndex).reduce((acc, curr) => acc + curr.analysedFilesBytes, 0),
|
||||
),
|
||||
|
||||
@ -3,7 +3,7 @@ import { LicenseService } from '@services/license.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { ChartDataset } from 'chart.js';
|
||||
import { ChartBlue, ChartGreen, ChartRed } from '../../utils/constants';
|
||||
import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { addPaddingToArray } from '@utils/functions';
|
||||
@ -58,7 +58,7 @@ export class LicensePageUsageComponent {
|
||||
order: 1,
|
||||
},
|
||||
{
|
||||
data: monthlyData.map(
|
||||
data: getDataUntilCurrentMonth(monthlyData).map(
|
||||
(month, monthIndex) =>
|
||||
month.numberOfAnalyzedPages +
|
||||
monthlyData.slice(0, monthIndex).reduce((acc, curr) => acc + curr.numberOfAnalyzedPages, 0),
|
||||
|
||||
@ -6,7 +6,7 @@ import { map } from 'rxjs/operators';
|
||||
import type { DonutChartConfig, ILicenseReport } from '@red/domain';
|
||||
import { ChartDataset } from 'chart.js';
|
||||
import { ChartBlack, ChartBlue, ChartGreen, ChartGrey, ChartRed } from '../../utils/constants';
|
||||
import { getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { getDataUntilCurrentMonth, getLabelsFromLicense, getLineConfig } from '../../utils/functions';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
@Component({
|
||||
@ -60,7 +60,7 @@ export class LicenseRetentionCapacityComponent {
|
||||
}
|
||||
|
||||
#getDatasets(license: ILicenseReport): ChartDataset[] {
|
||||
const monthlyData = license.monthlyData;
|
||||
const monthlyData = getDataUntilCurrentMonth(license.monthlyData);
|
||||
|
||||
return [
|
||||
{
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import { inject, NgModule } from '@angular/core';
|
||||
import { LicenseScreenComponent } from './license-screen/license-screen.component';
|
||||
import { LicenseSelectComponent } from './components/license-select/license-select.component';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { IqserHelpModeModule, IqserListingModule, SizePipe } from '@iqser/common-ui';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { inject, NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IqserHelpModeModule, IqserListingModule, SizePipe } from '@iqser/common-ui';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { LicenseService } from '@services/license.service';
|
||||
import { ChartComponent } from './components/chart/chart.component';
|
||||
import { NgChartsModule } from 'ng2-charts';
|
||||
import { LicenseRetentionCapacityComponent } from './components/license-retention-capacity-usage/license-retention-capacity.component';
|
||||
import { DonutChartComponent } from '@shared/components/donut-chart/donut-chart.component';
|
||||
import { LicensePageUsageComponent } from './components/license-page-usage/license-page-usage.component';
|
||||
import { BaseChartDirective, provideCharts, withDefaultRegisterables } from 'ng2-charts';
|
||||
import { ChartComponent } from './components/chart/chart.component';
|
||||
import { LicenseAnalysisCapacityUsageComponent } from './components/license-analysis-capacity-usage/license-analysis-capacity-usage.component';
|
||||
import { LicensePageUsageComponent } from './components/license-page-usage/license-page-usage.component';
|
||||
import { LicenseRetentionCapacityComponent } from './components/license-retention-capacity-usage/license-retention-capacity.component';
|
||||
import { LicenseSelectComponent } from './components/license-select/license-select.component';
|
||||
import { LicenseScreenComponent } from './license-screen/license-screen.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
@ -43,8 +43,9 @@ const routes: Routes = [
|
||||
IqserListingModule,
|
||||
IqserHelpModeModule,
|
||||
SizePipe,
|
||||
NgChartsModule,
|
||||
DonutChartComponent,
|
||||
BaseChartDirective,
|
||||
],
|
||||
providers: [provideCharts(withDefaultRegisterables())],
|
||||
})
|
||||
export class LicenseModule {}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { FillTarget } from 'chart.js';
|
||||
import { addPaddingToArray, hexToRgba } from '@utils/functions';
|
||||
import { ILicenseReport } from '@red/domain';
|
||||
import { ILicenseData, ILicenseReport } from '@red/domain';
|
||||
import { ComplexFillTarget } from 'chart.js/dist/types';
|
||||
|
||||
const monthNames = dayjs.monthsShort();
|
||||
const currentMonth = dayjs(Date.now()).month() + 1;
|
||||
|
||||
export const verboseDate = (date: Dayjs) => `${monthNames[date.month()]} ${date.year()}`;
|
||||
|
||||
@ -49,3 +50,7 @@ export const getLabelsFromLicense = (license: ILicenseReport) => {
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getDataUntilCurrentMonth = (monthlyData: ILicenseData[]) => {
|
||||
return monthlyData.slice(0, currentMonth);
|
||||
};
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
@use 'variables';
|
||||
|
||||
.right-container {
|
||||
display: flex;
|
||||
width: 353px;
|
||||
@ -15,21 +13,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .page-header .actions > *:not(:last-child) {
|
||||
margin-right: 6px;
|
||||
::ng-deep .page-header .actions {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.action-buttons > div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.cell {
|
||||
&.no-role-alignment {
|
||||
flex-direction: row;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
color: variables.$primary;
|
||||
}
|
||||
.cell.no-role-alignment {
|
||||
flex-direction: row;
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
|
||||
.opacity-1 {
|
||||
|
||||
@ -148,7 +148,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
|
||||
acceptResize() {
|
||||
if (this.resized) {
|
||||
return this.annotationActionsService.acceptResize(this.#annotations[0]);
|
||||
return this.annotationActionsService.acceptResize(this.#annotations[0], this.annotationPermissions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ interface Engine {
|
||||
readonly translateParams?: Record<string, any>;
|
||||
}
|
||||
|
||||
const Engines = {
|
||||
export const Engines = {
|
||||
DICTIONARY: 'DICTIONARY',
|
||||
NER: 'NER',
|
||||
RULE: 'RULE',
|
||||
@ -54,7 +54,7 @@ export class AnnotationDetailsComponent implements OnChanges {
|
||||
getChangesTooltip(): string | undefined {
|
||||
const changes = changesProperties.filter(key => this.annotation.item[key]);
|
||||
|
||||
if (!changes.length) {
|
||||
if (!changes.length || !this.annotation.item.engines?.includes(Engines.MANUAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -93,11 +93,6 @@ export class AnnotationDetailsComponent implements OnChanges {
|
||||
description: _('annotation-engines.imported'),
|
||||
show: isBasedOn(annotation, Engines.IMPORTED),
|
||||
},
|
||||
{
|
||||
icon: 'red:redaction-changes',
|
||||
description: _('annotation-engines.manual'),
|
||||
show: isBasedOn(annotation, Engines.MANUAL),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
<cdk-virtual-scroll-viewport [itemSize]="LIST_ITEM_SIZE" [ngStyle]="{ height: redactedTextsAreaHeight + 'px' }">
|
||||
<ul>
|
||||
<li *cdkVirtualFor="let value of values">{{ value }}</li>
|
||||
</ul>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
@ -0,0 +1,21 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 16px;
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { NgStyle } from '@angular/common';
|
||||
|
||||
const LIST_ITEM_SIZE = 16;
|
||||
const MAX_ITEMS_DISPLAY = 5;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-selected-annotations-list',
|
||||
standalone: true,
|
||||
imports: [CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, NgStyle],
|
||||
templateUrl: './selected-annotations-list.component.html',
|
||||
styleUrl: './selected-annotations-list.component.scss',
|
||||
})
|
||||
export class SelectedAnnotationsListComponent {
|
||||
@Input({ required: true }) values: string[];
|
||||
protected readonly LIST_ITEM_SIZE = LIST_ITEM_SIZE;
|
||||
|
||||
get redactedTextsAreaHeight() {
|
||||
return this.values.length <= MAX_ITEMS_DISPLAY ? LIST_ITEM_SIZE * this.values.length : LIST_ITEM_SIZE * MAX_ITEMS_DISPLAY;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th *ngFor="let column of columns" [ngClass]="{ hide: !column.show }">
|
||||
<label>{{ column.label }}</label>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody [ngStyle]="{ height: redactedTextsAreaHeight + 'px' }">
|
||||
<tr *ngFor="let row of data">
|
||||
<td *ngFor="let cell of row" [ngClass]="{ hide: !cell.show, bold: cell.bold }">
|
||||
{{ cell.label }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -0,0 +1,63 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
table {
|
||||
padding: 0 13px;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
border-spacing: 0;
|
||||
|
||||
tbody {
|
||||
padding-top: 2px;
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
tr {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
display: table;
|
||||
|
||||
th {
|
||||
label {
|
||||
opacity: 0.7;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
max-width: 0;
|
||||
width: 25%;
|
||||
text-align: start;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
th:last-child,
|
||||
td:last-child {
|
||||
max-width: 0;
|
||||
width: 50%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:nth-child(odd) {
|
||||
td {
|
||||
background-color: var(--iqser-alt-background);
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { NgClass, NgForOf, NgStyle } from '@angular/common';
|
||||
|
||||
export interface ValueColumn {
|
||||
label: string;
|
||||
show: boolean;
|
||||
bold?: boolean;
|
||||
}
|
||||
|
||||
const TABLE_ROW_SIZE = 18;
|
||||
const MAX_ITEMS_DISPLAY = 10;
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-selected-annotations-table',
|
||||
standalone: true,
|
||||
imports: [NgForOf, NgClass, NgStyle],
|
||||
templateUrl: './selected-annotations-table.component.html',
|
||||
styleUrl: './selected-annotations-table.component.scss',
|
||||
})
|
||||
export class SelectedAnnotationsTableComponent {
|
||||
@Input({ required: true }) columns: ValueColumn[];
|
||||
@Input({ required: true }) data: ValueColumn[][];
|
||||
|
||||
get redactedTextsAreaHeight() {
|
||||
return this.data.length <= MAX_ITEMS_DISPLAY ? TABLE_ROW_SIZE * this.data.length : TABLE_ROW_SIZE * MAX_ITEMS_DISPLAY;
|
||||
}
|
||||
}
|
||||
@ -5,14 +5,7 @@
|
||||
<div class="dialog-content redaction">
|
||||
<div class="iqser-input-group" *ngIf="showList">
|
||||
<label [translate]="'edit-redaction.dialog.content.redacted-text'" class="selected-text"></label>
|
||||
<cdk-virtual-scroll-viewport
|
||||
[itemSize]="16"
|
||||
[ngStyle]="{ height: redactedTexts.length <= 5 ? 16 * redactedTexts.length + 'px' : 80 + 'px' }"
|
||||
>
|
||||
<ul *cdkVirtualFor="let text of redactedTexts">
|
||||
<li>{{ text }}</li>
|
||||
</ul>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<redaction-selected-annotations-list [values]="redactedTexts"></redaction-selected-annotations-list>
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group required w-450">
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
padding-right: 10px;
|
||||
}
|
||||
@ -7,16 +7,8 @@
|
||||
></div>
|
||||
|
||||
<div class="dialog-content redaction" [class.fixed-height]="isRedacted && isImage">
|
||||
<div class="iqser-input-group" *ngIf="!isImage">
|
||||
<cdk-virtual-scroll-viewport
|
||||
*ngIf="!!redactedTexts.length && !allRectangles"
|
||||
[itemSize]="16"
|
||||
[ngStyle]="{ height: redactedTexts.length <= 5 ? 16 * redactedTexts.length + 'px' : 80 + 'px' }"
|
||||
>
|
||||
<ul *cdkVirtualFor="let text of redactedTexts">
|
||||
<li>{{ text }}</li>
|
||||
</ul>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
<div class="iqser-input-group" *ngIf="!isImage && redactedTexts.length && !allRectangles">
|
||||
<redaction-selected-annotations-list [values]="redactedTexts"></redaction-selected-annotations-list>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!isManualRedaction" class="iqser-input-group w-450" [class.required]="!form.controls.type.disabled">
|
||||
|
||||
@ -6,23 +6,3 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
<div *ngIf="isHintDialog" class="dialog-header heading-l" [translate]="'manual-annotation.dialog.header.force-hint'"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<redaction-selected-annotations-table [columns]="tableColumns" [data]="tableData"></redaction-selected-annotations-table>
|
||||
<div *ngIf="!isHintDialog && !isDocumine" class="iqser-input-group required w-400">
|
||||
<label [translate]="'manual-annotation.dialog.content.reason'"></label>
|
||||
<mat-form-field>
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { Roles } from '@users/roles';
|
||||
import { ValueColumn } from '../../components/selected-annotations-table/selected-annotations-table.component';
|
||||
|
||||
export interface LegalBasisOption {
|
||||
label?: string;
|
||||
@ -23,8 +24,24 @@ const DOCUMINE_LEGAL_BASIS = 'n-a.';
|
||||
})
|
||||
export class ForceAnnotationDialogComponent extends BaseDialogComponent implements OnInit {
|
||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||
protected readonly roles = Roles;
|
||||
|
||||
readonly tableColumns = [
|
||||
{
|
||||
label: 'Value',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
show: true,
|
||||
},
|
||||
];
|
||||
readonly tableData: ValueColumn[][] = this._data.annotations.map(redaction => [
|
||||
{ label: redaction.value, show: true, bold: true },
|
||||
{ label: redaction.typeLabel, show: true },
|
||||
]);
|
||||
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
protected readonly roles = Roles;
|
||||
|
||||
constructor(
|
||||
private readonly _justificationsService: JustificationsService,
|
||||
@ -33,7 +50,7 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
|
||||
private readonly _data: { readonly dossier: Dossier; readonly hint: boolean; annotations: AnnotationWrapper[] },
|
||||
) {
|
||||
super(_dialogRef);
|
||||
this.form = this._getForm();
|
||||
this.form = this.#getForm();
|
||||
}
|
||||
|
||||
get isHintDialog() {
|
||||
@ -66,17 +83,17 @@ export class ForceAnnotationDialogComponent extends BaseDialogComponent implemen
|
||||
}
|
||||
|
||||
save() {
|
||||
this._dialogRef.close(this._createForceRedactionRequest());
|
||||
this._dialogRef.close(this.#createForceRedactionRequest());
|
||||
}
|
||||
|
||||
private _getForm(): UntypedFormGroup {
|
||||
#getForm(): UntypedFormGroup {
|
||||
return this._formBuilder.group({
|
||||
reason: this._data.hint ? ['Forced Hint'] : [null, !this.isDocumine ? Validators.required : null],
|
||||
comment: [null],
|
||||
});
|
||||
}
|
||||
|
||||
private _createForceRedactionRequest(): ILegalBasisChangeRequest {
|
||||
#createForceRedactionRequest(): ILegalBasisChangeRequest {
|
||||
const request: ILegalBasisChangeRequest = {};
|
||||
|
||||
request.legalBasis = !this.isDocumine ? this.form.get('reason').value.legalBasis : DOCUMINE_LEGAL_BASIS;
|
||||
|
||||
@ -3,9 +3,8 @@
|
||||
<div [translate]="'redact-text.dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<div *ngIf="form.controls.selectedText.value as selectedText" class="iqser-input-group w-450">
|
||||
<label [translate]="'redact-text.dialog.content.selected-text'" class="selected-text"></label>
|
||||
{{ selectedText }}
|
||||
<div class="iqser-input-group">
|
||||
<redaction-selected-annotations-list [values]="selectedValues"></redaction-selected-annotations-list>
|
||||
</div>
|
||||
|
||||
<iqser-details-radio
|
||||
|
||||
@ -30,6 +30,7 @@ export class RedactRecommendationDialogComponent
|
||||
dictionaryRequest = false;
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
dictionaries: Dictionary[] = [];
|
||||
readonly selectedValues = this.data.annotations.map(annotation => annotation.value);
|
||||
readonly form = inject(FormBuilder).group({
|
||||
selectedText: this.isMulti ? null : this.firstEntry.value,
|
||||
comment: [null],
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
[class.fixed-height-36]="dictionaryRequest"
|
||||
[ngClass]="isEditingSelectedText ? 'flex relative' : 'flex-align-items-center'"
|
||||
>
|
||||
<span *ngIf="!isEditingSelectedText" [innerHTML]="form.controls.selectedText.value"></span>
|
||||
<ul>
|
||||
<li>
|
||||
<span *ngIf="!isEditingSelectedText" [innerHTML]="form.controls.selectedText.value"></span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<textarea
|
||||
*ngIf="isEditingSelectedText"
|
||||
|
||||
@ -7,24 +7,10 @@
|
||||
|
||||
<div [ngStyle]="{ height: dialogContentHeight + redactedTextsAreaHeight + 'px' }" class="dialog-content redaction">
|
||||
<div class="iqser-input-group">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th *ngFor="let column of columns()" [ngClass]="{ hide: !column.show }">
|
||||
<label>{{ column.label }}</label>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody [ngStyle]="{ height: redactedTextsAreaHeight + 'px' }">
|
||||
<tr *ngFor="let text of redactedTexts; let idx = index">
|
||||
<td>
|
||||
<b>{{ text }}</b>
|
||||
</td>
|
||||
<td>{{ data.redactions[idx].typeLabel }}</td>
|
||||
<td [ngClass]="{ hide: !isFalsePositive() }">{{ data.falsePositiveContext[idx] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<redaction-selected-annotations-table
|
||||
[columns]="tableColumns()"
|
||||
[data]="tableData()"
|
||||
></redaction-selected-annotations-table>
|
||||
</div>
|
||||
|
||||
<iqser-details-radio [options]="options" formControlName="option"></iqser-details-radio>
|
||||
|
||||
@ -1,64 +1,4 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
.dialog-content {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 35px;
|
||||
}
|
||||
|
||||
table {
|
||||
padding: 0 13px;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
border-spacing: 0;
|
||||
|
||||
tbody {
|
||||
padding-top: 2px;
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
tr {
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
display: table;
|
||||
|
||||
th {
|
||||
label {
|
||||
opacity: 0.7;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
max-width: 0;
|
||||
width: 25%;
|
||||
text-align: start;
|
||||
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
th:last-child,
|
||||
td:last-child {
|
||||
max-width: 0;
|
||||
width: 50%;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr:nth-child(odd) {
|
||||
td {
|
||||
background-color: var(--iqser-alt-background);
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@ -7,11 +7,7 @@ import { Roles } from '@users/roles';
|
||||
import { DialogHelpModeKeys } from '../../utils/constants';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
interface ValuesColumns {
|
||||
label: string;
|
||||
show: boolean;
|
||||
}
|
||||
import { ValueColumn } from '../../components/selected-annotations-table/selected-annotations-table.component';
|
||||
|
||||
@Component({
|
||||
templateUrl: './remove-redaction-dialog.component.html',
|
||||
@ -37,7 +33,7 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
|
||||
readonly selectedOption = toSignal(this.form.get('option').valueChanges.pipe(map(value => value.value)));
|
||||
readonly isFalsePositive = computed(() => this.selectedOption() === RemoveRedactionOptions.FALSE_POSITIVE);
|
||||
readonly columns = computed<ValuesColumns[]>(() => [
|
||||
readonly tableColumns = computed<ValueColumn[]>(() => [
|
||||
{
|
||||
label: 'Value',
|
||||
show: true,
|
||||
@ -52,6 +48,14 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
},
|
||||
]);
|
||||
|
||||
readonly tableData = computed<ValueColumn[][]>(() =>
|
||||
this.data.redactions.map((redaction, index) => [
|
||||
{ label: redaction.value, show: true, bold: true },
|
||||
{ label: redaction.typeLabel, show: true },
|
||||
{ label: this.data.falsePositiveContext[index], show: this.isFalsePositive() },
|
||||
]),
|
||||
);
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder) {
|
||||
super();
|
||||
}
|
||||
|
||||
@ -7,14 +7,18 @@
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<ng-container *ngIf="!redaction.isImage && !redaction.AREA">
|
||||
<div class="iqser-input-group w-450">
|
||||
<label [translate]="'resize-redaction.dialog.content.original-text'" class="selected-text"></label>
|
||||
<span>{{ redaction.value }}</span>
|
||||
<div class="flex-start">
|
||||
<label [translate]="'resize-redaction.dialog.content.original-text'"></label>
|
||||
<span class="multi-line-ellipsis"
|
||||
><b>{{ redaction.value }}</b></span
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group w-450">
|
||||
<label [translate]="'resize-redaction.dialog.content.resized-text'" class="selected-text"></label>
|
||||
<span>{{ data.text }}</span>
|
||||
<div class="flex-start">
|
||||
<label [translate]="'resize-redaction.dialog.content.resized-text'"></label>
|
||||
<span class="multi-line-ellipsis"
|
||||
><b>{{ data.text }}</b></span
|
||||
>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
.multi-line-ellipsis {
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.flex-start {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
padding: 0 13px;
|
||||
|
||||
label {
|
||||
opacity: 0.7;
|
||||
font-weight: normal;
|
||||
min-width: 15%;
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import { ResizeRedactionData, ResizeRedactionResult } from '../../utils/dialog-t
|
||||
|
||||
@Component({
|
||||
templateUrl: './resize-redaction-dialog.component.html',
|
||||
styleUrls: ['./resize-redaction-dialog.component.scss'],
|
||||
})
|
||||
export class ResizeRedactionDialogComponent extends IqserDialogComponent<
|
||||
ResizeRedactionDialogComponent,
|
||||
@ -29,7 +30,14 @@ export class ResizeRedactionDialogComponent extends IqserDialogComponent<
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder) {
|
||||
super();
|
||||
this.options = getResizeRedactionOptions(this.redaction, this.#dossier, false, this.#applyToAllDossiers, this.data.isApprover);
|
||||
this.options = getResizeRedactionOptions(
|
||||
this.redaction,
|
||||
this.#dossier,
|
||||
false,
|
||||
this.#applyToAllDossiers,
|
||||
this.data.isApprover,
|
||||
this.data.permissions.canResizeInDictionary,
|
||||
);
|
||||
this.form = this.#getForm();
|
||||
}
|
||||
|
||||
|
||||
@ -70,6 +70,8 @@ import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||
import { TablesService } from './services/tables.service';
|
||||
import { SelectedAnnotationsTableComponent } from './components/selected-annotations-table/selected-annotations-table.component';
|
||||
import { SelectedAnnotationsListComponent } from './components/selected-annotations-list/selected-annotations-list.component';
|
||||
import { FileHeaderComponent } from './components/file-header/file-header.component';
|
||||
import { DocumineExportComponent } from './components/documine-export/documine-export.component';
|
||||
import { StructuredComponentManagementComponent } from './components/structured-component-management/structured-component-management.component';
|
||||
@ -158,6 +160,8 @@ const components = [
|
||||
LogPipe,
|
||||
ReplaceNbspPipe,
|
||||
DisableStopPropagationDirective,
|
||||
SelectedAnnotationsTableComponent,
|
||||
SelectedAnnotationsListComponent,
|
||||
],
|
||||
providers: [FilePreviewDialogService, ManualRedactionService, DocumentUnloadedGuard, TablesService],
|
||||
})
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import { CommentsApiService } from '@services/comments-api.service';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { firstValueFrom, Observable, zip } from 'rxjs';
|
||||
import { firstValueFrom, Observable } from 'rxjs';
|
||||
import { getFirstRelevantTextPart } from '../../../utils';
|
||||
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
|
||||
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
|
||||
@ -32,6 +32,7 @@ import { ResizeRedactionDialogComponent } from '../dialogs/resize-redaction-dial
|
||||
import { RemoveRedactionOptions } from '../utils/dialog-options';
|
||||
import {
|
||||
EditRedactionData,
|
||||
EditRedactResult,
|
||||
RemoveRedactionData,
|
||||
RemoveRedactionPermissions,
|
||||
RemoveRedactionResult,
|
||||
@ -101,47 +102,34 @@ export class AnnotationActionsService {
|
||||
};
|
||||
|
||||
const result = await this.#getEditRedactionDialog(data).result();
|
||||
|
||||
const requests: Observable<unknown>[] = [];
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!this.#isDocumine &&
|
||||
(!annotations.every(annotation => annotation.legalBasis === result.legalBasis) ||
|
||||
!annotations.every(annotation => annotation.section === result.section))
|
||||
) {
|
||||
const changeLegalBasisBody = annotations.map(annotation => ({
|
||||
annotationId: annotation.id,
|
||||
legalBasis: result.legalBasis,
|
||||
section: result.section ?? annotation.section,
|
||||
value: result.value ?? annotation.value,
|
||||
}));
|
||||
requests.push(
|
||||
this._manualRedactionService.changeLegalBasis(
|
||||
changeLegalBasisBody,
|
||||
dossierId,
|
||||
fileId,
|
||||
file().excludedFromAutomaticAnalysis && isUnprocessed,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (result.type && !annotations.every(annotation => annotation.type === result.type)) {
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => ({
|
||||
annotationId: annotation.id,
|
||||
type: result.type ?? annotation.type,
|
||||
}));
|
||||
requests.push(
|
||||
this._manualRedactionService.recategorizeRedactions(
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => {
|
||||
const body = { annotationId: annotation.id, type: result.type ?? annotation.type };
|
||||
if (!this.#isDocumine) {
|
||||
return {
|
||||
...body,
|
||||
legalBasis: result.legalBasis,
|
||||
section: result.section ?? annotation.section,
|
||||
value: result.value ?? annotation.value,
|
||||
};
|
||||
}
|
||||
return body;
|
||||
});
|
||||
|
||||
await this.#processObsAndEmit(
|
||||
this._manualRedactionService
|
||||
.recategorizeRedactions(
|
||||
recategorizeBody,
|
||||
dossierId,
|
||||
fileId,
|
||||
this.#getChangedFields(annotations, result),
|
||||
file().excludedFromAutomaticAnalysis && isUnprocessed,
|
||||
),
|
||||
);
|
||||
}
|
||||
)
|
||||
.pipe(log()),
|
||||
);
|
||||
|
||||
if (result.comment) {
|
||||
try {
|
||||
@ -152,11 +140,6 @@ export class AnnotationActionsService {
|
||||
this._toaster.rawError(error.error.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!requests.length) {
|
||||
return;
|
||||
}
|
||||
await this.#processObsAndEmit(zip(requests).pipe(log()));
|
||||
}
|
||||
|
||||
async removeRedaction(redactions: AnnotationWrapper[], permissions: AnnotationPermissions) {
|
||||
@ -240,7 +223,7 @@ export class AnnotationActionsService {
|
||||
this._annotationManager.select(viewerAnnotation);
|
||||
}
|
||||
|
||||
async acceptResize(annotation: AnnotationWrapper): Promise<void> {
|
||||
async acceptResize(annotation: AnnotationWrapper, permissions: AnnotationPermissions): Promise<void> {
|
||||
const textAndPositions = await this.#extractTextAndPositions(annotation.id);
|
||||
if (annotation.isRecommendation) {
|
||||
const recommendation = {
|
||||
@ -263,12 +246,16 @@ export class AnnotationActionsService {
|
||||
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
||||
const isUnprocessed = annotation.pending;
|
||||
|
||||
const data = {
|
||||
const data: ResizeRedactionData = {
|
||||
redaction: annotation,
|
||||
text,
|
||||
applyToAllDossiers: isApprover && dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault,
|
||||
isApprover,
|
||||
dossierId: dossier.dossierId,
|
||||
permissions: {
|
||||
canResizeAnnotation: permissions.canResizeAnnotation,
|
||||
canResizeInDictionary: permissions.canResizeInDictionary,
|
||||
},
|
||||
};
|
||||
|
||||
const result = await this.#getResizeRedactionDialog(data).result();
|
||||
@ -498,4 +485,23 @@ export class AnnotationActionsService {
|
||||
isApprover,
|
||||
};
|
||||
}
|
||||
|
||||
#getChangedFields(annotations: AnnotationWrapper[], result: EditRedactResult) {
|
||||
const changedFields = [];
|
||||
if (result.type && !annotations.every(annotation => annotation.type === result.type)) {
|
||||
changedFields.push('type');
|
||||
}
|
||||
|
||||
if (this.#isDocumine) {
|
||||
return { changes: changedFields.join(', ') };
|
||||
}
|
||||
|
||||
if (result.legalBasis && !annotations.every(annotation => annotation.legalBasis === result.legalBasis)) {
|
||||
changedFields.push('reason');
|
||||
}
|
||||
if (typeof result.section === 'string' && !annotations.every(annotation => annotation.section === result.section)) {
|
||||
changedFields.push('paragraph/location');
|
||||
}
|
||||
return { changes: changedFields.join(', ') };
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import {
|
||||
} from '../utils/sort-by-page-rotation.utils';
|
||||
import { FileDataService } from './file-data.service';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
import { Engines } from '../components/annotation-details/annotation-details.component';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationProcessingService {
|
||||
@ -50,7 +51,8 @@ export class AnnotationProcessingService {
|
||||
label: _('filter-menu.redaction-changes'),
|
||||
checked: false,
|
||||
topLevelFilter: true,
|
||||
checker: (annotation: AnnotationWrapper) => annotation?.hasRedactionChanges,
|
||||
checker: (annotation: AnnotationWrapper) =>
|
||||
annotation?.hasRedactionChanges && annotation?.engines?.includes(Engines.MANUAL),
|
||||
},
|
||||
{
|
||||
id: 'unseen-pages',
|
||||
|
||||
@ -18,7 +18,7 @@ import type {
|
||||
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
|
||||
import { Roles } from '@users/roles';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { EMPTY, of, OperatorFunction, pipe } from 'rxjs';
|
||||
import { EMPTY, of, pipe } from 'rxjs';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
|
||||
function getResponseType(error: boolean, isConflict: boolean) {
|
||||
@ -62,12 +62,18 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
return this.addAnnotation(recommendations, dossierId, fileId);
|
||||
}
|
||||
|
||||
changeLegalBasis(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
|
||||
return this.legalBasisChange(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-legal-basis'));
|
||||
}
|
||||
|
||||
recategorizeRedactions(body: List<IRecategorizationRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
|
||||
return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(this.#showToast('change-type'));
|
||||
recategorizeRedactions(
|
||||
body: List<IRecategorizationRequest>,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
successMessageParameters?: {
|
||||
[key: string]: string;
|
||||
},
|
||||
includeUnprocessed = false,
|
||||
) {
|
||||
return this.recategorize(body, dossierId, fileId, includeUnprocessed).pipe(
|
||||
this.#showToast('recategorize-annotation', false, successMessageParameters),
|
||||
);
|
||||
}
|
||||
|
||||
addAnnotation(
|
||||
@ -131,13 +137,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
);
|
||||
}
|
||||
|
||||
legalBasisChange(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string, includeUnprocessed = false) {
|
||||
return this._post(
|
||||
body,
|
||||
`${this.#bulkRedaction}/legalBasisChange/${dossierId}/${fileId}?includeUnprocessed=${includeUnprocessed}`,
|
||||
).pipe(this.#log('Legal basis change', body));
|
||||
}
|
||||
|
||||
undo(annotationIds: List, dossierId: string, fileId: string) {
|
||||
const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`;
|
||||
return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds));
|
||||
@ -165,7 +164,11 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
});
|
||||
}
|
||||
|
||||
#showToast(action: ManualRedactionActions | DictionaryActions, isDictionary = false) {
|
||||
#showToast(
|
||||
action: ManualRedactionActions | DictionaryActions,
|
||||
isDictionary = false,
|
||||
successMessageParameters?: { [key: string]: string },
|
||||
) {
|
||||
return pipe(
|
||||
catchError((error: unknown) => {
|
||||
const isConflict = (error as HttpErrorResponse).status === HttpStatusCode.Conflict;
|
||||
@ -175,7 +178,12 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
});
|
||||
return EMPTY;
|
||||
}),
|
||||
tap(() => this._toaster.success(getMessage(action, isDictionary), { positionClass: 'toast-file-preview' })),
|
||||
tap(() =>
|
||||
this._toaster.success(getMessage(action, isDictionary), {
|
||||
params: successMessageParameters,
|
||||
positionClass: 'toast-file-preview',
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ export class PdfAnnotationActionsService {
|
||||
let acceptResizeButton: IHeaderElement;
|
||||
if (this.#annotationManager.annotationHasBeenResized) {
|
||||
acceptResizeButton = this.#getButton('check', _('annotation-actions.resize-accept.label'), () =>
|
||||
this.#annotationActionsService.acceptResize(firstAnnotation),
|
||||
this.#annotationActionsService.acceptResize(firstAnnotation, permissions),
|
||||
);
|
||||
} else {
|
||||
acceptResizeButton = this.#getDisabledCheckButton();
|
||||
|
||||
@ -113,6 +113,7 @@ export const getResizeRedactionOptions = (
|
||||
isRss: boolean,
|
||||
applyToAllDossiers: boolean,
|
||||
isApprover: boolean,
|
||||
canResizeInDictionary: boolean,
|
||||
): DetailsRadioOption<ResizeRedactionOption>[] => {
|
||||
const translations = resizeRedactionTranslations;
|
||||
const options: DetailsRadioOption<ResizeRedactionOption>[] = [
|
||||
@ -127,7 +128,7 @@ export const getResizeRedactionOptions = (
|
||||
if (isRss) {
|
||||
return options;
|
||||
}
|
||||
if (!redaction.hasBeenResizedLocally) {
|
||||
if (canResizeInDictionary) {
|
||||
const dictBasedType = redaction.isModifyDictionary;
|
||||
options.push({
|
||||
label: translations.inDossier.label,
|
||||
|
||||
@ -46,6 +46,11 @@ export interface EditRedactResult {
|
||||
export type AddHintResult = RedactTextResult;
|
||||
export type AddAnnotationResult = RedactTextResult;
|
||||
|
||||
export interface ResizeRedactionPermissions {
|
||||
canResizeAnnotation: boolean;
|
||||
canResizeInDictionary: boolean;
|
||||
}
|
||||
|
||||
export interface ResizeAnnotationData {
|
||||
redaction: AnnotationWrapper;
|
||||
text: string;
|
||||
@ -55,6 +60,7 @@ export interface ResizeAnnotationData {
|
||||
export interface ResizeRedactionData extends ResizeAnnotationData {
|
||||
applyToAllDossiers?: boolean;
|
||||
isApprover?: boolean;
|
||||
permissions: ResizeRedactionPermissions;
|
||||
}
|
||||
|
||||
export interface ResizeAnnotationResult {
|
||||
|
||||
@ -26,7 +26,7 @@ export class EntityLogService extends GenericService<unknown> {
|
||||
|
||||
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
|
||||
return entityLogEntry.filter(entry => {
|
||||
entry.positions = entry.positions.filter(p => !!p.rectangle?.length);
|
||||
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);
|
||||
const hasPositions = !!entry.positions?.length;
|
||||
const isRemoved = entry.state === EntryStates.REMOVED;
|
||||
if (!hasPositions) {
|
||||
|
||||
@ -28,10 +28,6 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
||||
error: _('annotation-actions.message.manual-redaction.add.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.add.success'),
|
||||
},
|
||||
'change-legal-basis': {
|
||||
error: _('annotation-actions.message.manual-redaction.change-legal-basis.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.change-legal-basis.success'),
|
||||
},
|
||||
'force-redaction': {
|
||||
error: _('annotation-actions.message.manual-redaction.force-redaction.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.force-redaction.success'),
|
||||
@ -44,9 +40,9 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
||||
error: _('annotation-actions.message.manual-redaction.recategorize-image.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.recategorize-image.success'),
|
||||
},
|
||||
'change-type': {
|
||||
error: _('annotation-actions.message.manual-redaction.change-type.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.change-type.success'),
|
||||
'recategorize-annotation': {
|
||||
error: _('annotation-actions.message.manual-redaction.recategorize-annotation.error'),
|
||||
success: _('annotation-actions.message.manual-redaction.recategorize-annotation.success'),
|
||||
},
|
||||
undo: {
|
||||
error: _('annotation-actions.message.manual-redaction.undo.error'),
|
||||
|
||||
@ -288,14 +288,6 @@
|
||||
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
||||
"success": "Schwärzung hinzugefügt!"
|
||||
},
|
||||
"change-legal-basis": {
|
||||
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
||||
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
||||
},
|
||||
"change-type": {
|
||||
"error": "Failed to edit type: {error}",
|
||||
"success": "Type was edited."
|
||||
},
|
||||
"force-hint": {
|
||||
"error": "Failed to save hint: {error}",
|
||||
"success": "Hint added!"
|
||||
@ -304,6 +296,10 @@
|
||||
"error": "Die Schwärzung konnte nicht gespeichert werden!",
|
||||
"success": "Schwärzung eingefügt!"
|
||||
},
|
||||
"recategorize-annotation": {
|
||||
"error": "Failed to edit type: {error}",
|
||||
"success": "Annotation was edited: Changed {changes}."
|
||||
},
|
||||
"recategorize-image": {
|
||||
"error": "Rekategorisierung des Bildes gescheitert: {error}",
|
||||
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
||||
@ -355,7 +351,7 @@
|
||||
"annotation-engines": {
|
||||
"dictionary": "{isHint, select, true{Hint} other{Redaction}} basierend auf Wörterbuch",
|
||||
"imported": "Imported",
|
||||
"manual": "",
|
||||
"manual": "Manual",
|
||||
"ner": "Redaktion basierend auf KI",
|
||||
"rule": "Schwärzung basierend auf Regel {rule}"
|
||||
},
|
||||
@ -1985,7 +1981,6 @@
|
||||
"reason": "Reason",
|
||||
"reason-placeholder": "Select a reason...",
|
||||
"revert-text": "Revert to selected text",
|
||||
"selected-text": "Selected text:",
|
||||
"type": "Type",
|
||||
"type-placeholder": "Select type..."
|
||||
},
|
||||
|
||||
@ -288,14 +288,6 @@
|
||||
"error": "Failed to save redaction: {error}",
|
||||
"success": "Redaction added!"
|
||||
},
|
||||
"change-legal-basis": {
|
||||
"error": "Failed to edit annotation reason: {error}",
|
||||
"success": "Annotation reason was edited."
|
||||
},
|
||||
"change-type": {
|
||||
"error": "Failed to edit type: {error}",
|
||||
"success": "Type was edited."
|
||||
},
|
||||
"force-hint": {
|
||||
"error": "Failed to save hint: {error}",
|
||||
"success": "Hint added!"
|
||||
@ -304,6 +296,10 @@
|
||||
"error": "Failed to save redaction: {error}",
|
||||
"success": "Redaction added!"
|
||||
},
|
||||
"recategorize-annotation": {
|
||||
"error": "Failed to edit type: {error}",
|
||||
"success": "Annotation was edited: Changed {changes}."
|
||||
},
|
||||
"recategorize-image": {
|
||||
"error": "Failed to recategorize image: {error}",
|
||||
"success": "Image recategorized."
|
||||
@ -355,7 +351,6 @@
|
||||
"annotation-engines": {
|
||||
"dictionary": "Based on dictionary",
|
||||
"imported": "Imported",
|
||||
"manual": "Manual",
|
||||
"ner": "Based on AI",
|
||||
"rule": "Based on rule"
|
||||
},
|
||||
@ -1985,7 +1980,6 @@
|
||||
"reason": "Reason",
|
||||
"reason-placeholder": "Select a reason...",
|
||||
"revert-text": "Revert to selected text",
|
||||
"selected-text": "Selected text:",
|
||||
"type": "Type",
|
||||
"type-placeholder": "Select type..."
|
||||
},
|
||||
|
||||
@ -288,14 +288,6 @@
|
||||
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
||||
"success": "Schwärzung hinzugefügt!"
|
||||
},
|
||||
"change-legal-basis": {
|
||||
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
||||
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
||||
},
|
||||
"change-type": {
|
||||
"error": "",
|
||||
"success": ""
|
||||
},
|
||||
"force-hint": {
|
||||
"error": "Failed to save hint: {error}",
|
||||
"success": "Hint added!"
|
||||
@ -304,6 +296,10 @@
|
||||
"error": "Die Schwärzung konnte nicht gespeichert werden!",
|
||||
"success": "Schwärzung eingefügt!"
|
||||
},
|
||||
"recategorize-annotation": {
|
||||
"error": "",
|
||||
"success": ""
|
||||
},
|
||||
"recategorize-image": {
|
||||
"error": "Rekategorisierung des Bildes gescheitert: {error}",
|
||||
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
||||
@ -355,7 +351,7 @@
|
||||
"annotation-engines": {
|
||||
"dictionary": "{isHint, select, true{Hint} other{Redaction}} basierend auf Wörterbuch",
|
||||
"imported": "Annotation is imported",
|
||||
"manual": "",
|
||||
"manual": "Manual",
|
||||
"ner": "Redaktion basierend auf KI",
|
||||
"rule": "Schwärzung basierend auf Regel {rule}"
|
||||
},
|
||||
@ -1985,7 +1981,6 @@
|
||||
"reason": "Reason",
|
||||
"reason-placeholder": "Select a reasons...",
|
||||
"revert-text": "",
|
||||
"selected-text": "Selected text:",
|
||||
"type": "Type",
|
||||
"type-placeholder": "Select type..."
|
||||
},
|
||||
|
||||
@ -288,14 +288,6 @@
|
||||
"error": "Failed to save annotation: {error}",
|
||||
"success": "Annotation added!"
|
||||
},
|
||||
"change-legal-basis": {
|
||||
"error": "Failed to edit annotation reason: {error}",
|
||||
"success": "Annotation reason was edited."
|
||||
},
|
||||
"change-type": {
|
||||
"error": "",
|
||||
"success": ""
|
||||
},
|
||||
"force-hint": {
|
||||
"error": "Failed to save hint: {error}",
|
||||
"success": "Hint added!"
|
||||
@ -304,6 +296,10 @@
|
||||
"error": "Failed to save annotation: {error}",
|
||||
"success": "Annotation added!"
|
||||
},
|
||||
"recategorize-annotation": {
|
||||
"error": "",
|
||||
"success": ""
|
||||
},
|
||||
"recategorize-image": {
|
||||
"error": "Failed to recategorize image: {error}",
|
||||
"success": "Image recategorized."
|
||||
@ -355,7 +351,6 @@
|
||||
"annotation-engines": {
|
||||
"dictionary": "{isHint, select, true{Hint} other{Annotation}} based on dictionary",
|
||||
"imported": "Annotation is imported",
|
||||
"manual": "Manual",
|
||||
"ner": "Annotation based on AI",
|
||||
"rule": "Annotation based on rule {rule}"
|
||||
},
|
||||
@ -1985,7 +1980,6 @@
|
||||
"reason": "Reason",
|
||||
"reason-placeholder": "Select a reasons...",
|
||||
"revert-text": "",
|
||||
"selected-text": "Selected text:",
|
||||
"type": "Type",
|
||||
"type-placeholder": "Select type..."
|
||||
},
|
||||
|
||||
@ -6,12 +6,11 @@ export type ManualRedactionActions =
|
||||
| 'add'
|
||||
| 'remove'
|
||||
| 'remove-hint'
|
||||
| 'change-legal-basis'
|
||||
| 'recategorize-image'
|
||||
| 'undo'
|
||||
| 'force-redaction'
|
||||
| 'force-hint'
|
||||
| 'change-type';
|
||||
| 'recategorize-annotation';
|
||||
|
||||
export const AnnotationIconTypes = {
|
||||
square: 'square',
|
||||
|
||||
@ -2,4 +2,7 @@ export interface IRecategorizationRequest {
|
||||
readonly annotationId?: string;
|
||||
readonly comment?: string;
|
||||
readonly type?: string;
|
||||
readonly legalBasis?: string;
|
||||
readonly section?: string;
|
||||
readonly value?: string;
|
||||
}
|
||||
|
||||
100
package.json
100
package.json
@ -19,84 +19,84 @@
|
||||
"*.{ts,js,html}": "eslint --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "17.0.5",
|
||||
"@angular/cdk": "17.0.1",
|
||||
"@angular/common": "17.0.5",
|
||||
"@angular/compiler": "17.0.5",
|
||||
"@angular/core": "17.0.5",
|
||||
"@angular/forms": "17.0.5",
|
||||
"@angular/material": "17.0.1",
|
||||
"@angular/platform-browser": "17.0.5",
|
||||
"@angular/platform-browser-dynamic": "17.0.5",
|
||||
"@angular/router": "17.0.5",
|
||||
"@angular/service-worker": "17.0.5",
|
||||
"@angular/animations": "17.3.4",
|
||||
"@angular/cdk": "17.3.4",
|
||||
"@angular/common": "17.3.4",
|
||||
"@angular/compiler": "17.3.4",
|
||||
"@angular/core": "17.3.4",
|
||||
"@angular/forms": "17.3.4",
|
||||
"@angular/material": "17.3.4",
|
||||
"@angular/platform-browser": "17.3.4",
|
||||
"@angular/platform-browser-dynamic": "17.3.4",
|
||||
"@angular/router": "17.3.4",
|
||||
"@angular/service-worker": "17.3.4",
|
||||
"@materia-ui/ngx-monaco-editor": "^6.0.0",
|
||||
"@messageformat/core": "^3.3.0",
|
||||
"@ngx-translate/core": "15.0.0",
|
||||
"@ngx-translate/http-loader": "8.0.0",
|
||||
"@pdftron/webviewer": "10.5.0",
|
||||
"chart.js": "4.4.0",
|
||||
"@pdftron/webviewer": "10.8.0",
|
||||
"chart.js": "4.4.2",
|
||||
"dayjs": "1.11.10",
|
||||
"file-saver": "^2.0.5",
|
||||
"jszip": "^3.10.1",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"keycloak-angular": "15.0.0",
|
||||
"keycloak-angular": "15.1.0",
|
||||
"keycloak-js": "23.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"monaco-editor": "0.44.0",
|
||||
"ng2-charts": "5.0.3",
|
||||
"monaco-editor": "0.47.0",
|
||||
"ng2-charts": "6.0.0",
|
||||
"ngx-color-picker": "16.0.0",
|
||||
"ngx-logger": "^5.0.11",
|
||||
"ngx-toastr": "18.0.0",
|
||||
"ngx-translate-messageformat-compiler": "6.5.0",
|
||||
"ngx-translate-messageformat-compiler": "7.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"papaparse": "^5.4.0",
|
||||
"rxjs": "7.8.1",
|
||||
"sass": "1.69.5",
|
||||
"sass": "1.75.0",
|
||||
"scroll-into-view-if-needed": "3.1.0",
|
||||
"streamsaver": "^2.0.5",
|
||||
"tslib": "2.6.2",
|
||||
"zone.js": "0.14.2"
|
||||
"zone.js": "0.14.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "17.0.4",
|
||||
"@angular-devkit/core": "17.0.4",
|
||||
"@angular-devkit/schematics": "17.0.4",
|
||||
"@angular-eslint/builder": "17.1.0",
|
||||
"@angular-eslint/eslint-plugin": "17.1.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.1.0",
|
||||
"@angular-eslint/schematics": "17.1.0",
|
||||
"@angular-eslint/template-parser": "17.1.0",
|
||||
"@angular/cli": "17.0.4",
|
||||
"@angular/compiler-cli": "17.0.5",
|
||||
"@angular/language-service": "17.0.5",
|
||||
"@angular-devkit/build-angular": "17.3.4",
|
||||
"@angular-devkit/core": "17.3.4",
|
||||
"@angular-devkit/schematics": "17.3.4",
|
||||
"@angular-eslint/builder": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.3.0",
|
||||
"@angular-eslint/schematics": "17.3.0",
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "17.3.4",
|
||||
"@angular/compiler-cli": "17.3.4",
|
||||
"@angular/language-service": "17.3.4",
|
||||
"@bartholomej/ngx-translate-extract": "^8.0.2",
|
||||
"@localazy/ts-api": "^1.0.0",
|
||||
"@schematics/angular": "17.0.4",
|
||||
"@schematics/angular": "17.3.4",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/jest": "29.5.10",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/lodash-es": "4.17.12",
|
||||
"@types/node": "20.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"axios": "1.6.2",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-prettier": "5.0.1",
|
||||
"@types/node": "20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"axios": "1.6.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-prettier": "5.1.3",
|
||||
"eslint-plugin-rxjs": "^5.0.2",
|
||||
"google-translate-api-browser": "^4.1.9",
|
||||
"husky": "^8.0.3",
|
||||
"google-translate-api-browser": "^5.0.0",
|
||||
"husky": "^9.0.11",
|
||||
"jest": "29.7.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jest-extended": "4.0.2",
|
||||
"jest-preset-angular": "13.1.4",
|
||||
"lint-staged": "15.1.0",
|
||||
"prettier": "3.1.0",
|
||||
"sonarqube-scanner": "3.3.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.2.2",
|
||||
"webpack": "5.89.0",
|
||||
"webpack-bundle-analyzer": "4.10.1",
|
||||
"xliff": "^6.1.0"
|
||||
"jest-preset-angular": "14.0.3",
|
||||
"lint-staged": "15.2.2",
|
||||
"prettier": "3.2.5",
|
||||
"sonarqube-scanner": "3.4.0",
|
||||
"ts-node": "10.9.2",
|
||||
"typescript": "5.4.5",
|
||||
"webpack": "5.91.0",
|
||||
"webpack-bundle-analyzer": "4.10.2",
|
||||
"xliff": "^6.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user