Merge branch 'master' into DM-370
This commit is contained in:
commit
b4892afff5
@ -29,6 +29,7 @@ export class AnnotationPermissions {
|
||||
canResizeAnnotation = true;
|
||||
canRecategorizeAnnotation = true;
|
||||
canForceHint = true;
|
||||
canEditAnnotations = true;
|
||||
|
||||
static forUser(
|
||||
isApprover: boolean,
|
||||
@ -58,6 +59,7 @@ export class AnnotationPermissions {
|
||||
permissions.canChangeLegalBasis = canChangeLegalBasis(annotation, canAddRedaction);
|
||||
permissions.canRecategorizeAnnotation = canRecategorizeAnnotation(annotation, canAddRedaction);
|
||||
permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction);
|
||||
permissions.canEditAnnotations = annotation.isSkipped || annotation.isRedacted;
|
||||
|
||||
summedPermissions._merge(permissions);
|
||||
}
|
||||
@ -77,6 +79,7 @@ export class AnnotationPermissions {
|
||||
result.canRemoveOnlyHere = permissions.reduce((acc, next) => acc && next.canRemoveOnlyHere, true);
|
||||
result.canRemoveRedaction = permissions.reduce((acc, next) => acc && next.canRemoveRedaction, true);
|
||||
result.canUndo = permissions.reduce((acc, next) => acc && next.canUndo, true);
|
||||
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -13,27 +13,35 @@ import ICodeEditor = monaco.editor.ICodeEditor;
|
||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
|
||||
|
||||
interface SyntaxError {
|
||||
line: number;
|
||||
column: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './rules-screen.component.html',
|
||||
styleUrls: ['./rules-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||
theme: 'vs',
|
||||
language: 'java',
|
||||
automaticLayout: true,
|
||||
readOnly: !this.permissionsService.canEditRules(),
|
||||
glyphMargin: true,
|
||||
};
|
||||
initialLines: string[] = [];
|
||||
currentLines: string[] = [];
|
||||
isLeaving = false;
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
private _errorGlyphs: string[] = [];
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
@ -92,9 +100,12 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
).then(
|
||||
async () => {
|
||||
await this._initialize();
|
||||
this._removeErrorMarkers();
|
||||
this._toaster.success(_('rules-screen.success.generic'));
|
||||
},
|
||||
() => {
|
||||
error => {
|
||||
const errors = error.error as SyntaxError[] | undefined;
|
||||
this._drawErrorMarkers(errors);
|
||||
this._loadingService.stop();
|
||||
this._toaster.error(_('rules-screen.error.generic'));
|
||||
},
|
||||
@ -104,6 +115,7 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
revert(): void {
|
||||
this.currentLines = this.initialLines;
|
||||
this._decorations = this._codeEditor?.deltaDecorations(this._decorations, []) || [];
|
||||
this._removeErrorMarkers();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
@ -142,6 +154,45 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
} as IModelDeltaDecoration;
|
||||
}
|
||||
|
||||
private _drawErrorMarkers(errors: SyntaxError[] | undefined) {
|
||||
const model = this._codeEditor?.getModel();
|
||||
if (!model || !errors?.length) {
|
||||
return;
|
||||
}
|
||||
const markers = [];
|
||||
const glyphs = [];
|
||||
errors
|
||||
.filter(e => e.line > 0)
|
||||
.forEach(e => {
|
||||
const endColumn = model.getLineLength(e.line) + 1;
|
||||
markers.push({
|
||||
message: e.message,
|
||||
severity: monaco.MarkerSeverity.Error,
|
||||
startLineNumber: e.line,
|
||||
startColumn: e.column,
|
||||
endLineNumber: e.line,
|
||||
endColumn,
|
||||
});
|
||||
glyphs.push({
|
||||
range: new monaco.Range(e.line, e.column, e.line, endColumn),
|
||||
options: {
|
||||
glyphMarginClassName: 'error-glyph-margin',
|
||||
},
|
||||
});
|
||||
});
|
||||
this._errorGlyphs = this._codeEditor.deltaDecorations(this._errorGlyphs, glyphs);
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, markers);
|
||||
}
|
||||
|
||||
private _removeErrorMarkers() {
|
||||
const model = this._codeEditor?.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, []);
|
||||
this._errorGlyphs = this._codeEditor?.deltaDecorations(this._errorGlyphs, []) || [];
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
this._loadingService.start();
|
||||
await firstValueFrom(this._rulesService.download(this.#dossierTemplateId)).then(
|
||||
|
||||
@ -35,12 +35,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.editRedaction(annotations)"
|
||||
*ngIf="
|
||||
annotationPermissions.canChangeLegalBasis ||
|
||||
annotationPermissions.canRecategorizeAnnotation ||
|
||||
annotationPermissions.canForceRedaction ||
|
||||
annotationPermissions.canForceHint
|
||||
"
|
||||
*ngIf="canEdit"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
|
||||
[type]="buttonType"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, computed, Input, OnChanges } from '@angular/core';
|
||||
import { HelpModeService, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { getConfig, HelpModeService, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -37,6 +37,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
const hidden = this._annotationManager.hidden();
|
||||
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
|
||||
});
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
constructor(
|
||||
readonly viewModeService: ViewModeService,
|
||||
@ -54,6 +55,15 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
return this.#annotations;
|
||||
}
|
||||
|
||||
get canEdit() {
|
||||
const canEditRedactions =
|
||||
this.annotationPermissions.canChangeLegalBasis ||
|
||||
this.annotationPermissions.canRecategorizeAnnotation ||
|
||||
this.annotationPermissions.canForceHint ||
|
||||
this.annotationPermissions.canForceRedaction;
|
||||
return this.#isDocumine && this.annotations.length > 1 ? this.annotationPermissions.canEditAnnotations : canEditRedactions;
|
||||
}
|
||||
|
||||
@Input()
|
||||
set annotations(annotations: AnnotationWrapper[]) {
|
||||
this.#annotations = annotations.filter(a => a !== undefined);
|
||||
|
||||
@ -3,16 +3,26 @@
|
||||
<div [translate]="'edit-redaction.dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<div *ngIf="redactedText" class="iqser-input-group w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.redacted-text'" class="selected-text"></label>
|
||||
{{ redactedText }}
|
||||
<div *ngIf="showList">
|
||||
<label
|
||||
[translate]="'edit-redaction.dialog.content.redacted-text'"
|
||||
[translateParams]="{ length: redactedTexts.length }"
|
||||
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>
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group required w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.type'"></label>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="type">
|
||||
<mat-select formControlName="type" [placeholder]="'edit-redaction.dialog.content.unchanged' | translate">
|
||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
||||
<mat-option
|
||||
*ngFor="let dictionary of dictionaries"
|
||||
@ -29,7 +39,11 @@
|
||||
<div class="iqser-input-group w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.comment'"></label>
|
||||
<textarea
|
||||
[placeholder]="'edit-redaction.dialog.content.comment-placeholder' | translate"
|
||||
[placeholder]="
|
||||
redactedTexts.length === 1
|
||||
? ('edit-redaction.dialog.content.comment-placeholder' | translate)
|
||||
: ('edit-redaction.dialog.content.unchanged' | translate)
|
||||
"
|
||||
formControlName="comment"
|
||||
iqserHasScrollbar
|
||||
name="comment"
|
||||
@ -40,7 +54,12 @@
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<iqser-icon-button [label]="'edit-redaction.dialog.actions.save' | translate" [submit]="true" [type]="iconButtonTypes.primary">
|
||||
<iqser-icon-button
|
||||
[label]="'edit-redaction.dialog.actions.save' | translate"
|
||||
[submit]="true"
|
||||
[type]="iconButtonTypes.primary"
|
||||
[disabled]="!changed"
|
||||
>
|
||||
</iqser-icon-button>
|
||||
|
||||
<div [translate]="'edit-redaction.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
cdk-virtual-scroll-viewport {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { EditRedactionData, EditRedactResult } from '../../../utils/dialog-types
|
||||
|
||||
@Component({
|
||||
templateUrl: 'edit-annotation-dialog.component.html',
|
||||
styleUrls: ['edit-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class EditAnnotationDialogComponent
|
||||
extends IqserDialogComponent<EditAnnotationDialogComponent, EditRedactionData, EditRedactResult>
|
||||
@ -17,7 +18,7 @@ export class EditAnnotationDialogComponent
|
||||
{
|
||||
readonly roles = Roles;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly redactedText: string;
|
||||
readonly redactedTexts: string[];
|
||||
dictionaries: Dictionary[] = [];
|
||||
form: UntypedFormGroup;
|
||||
readonly #dossier: Dossier;
|
||||
@ -32,9 +33,9 @@ export class EditAnnotationDialogComponent
|
||||
super();
|
||||
this.#dossier = _activeDossiersService.find(this.data.dossierId);
|
||||
const annotations = this.data.annotations;
|
||||
const firstEntry = annotations[0];
|
||||
this.redactedText = annotations.length === 1 ? firstEntry.value : null;
|
||||
this.redactedTexts = annotations.map(annotation => annotation.value);
|
||||
this.form = this.#getForm();
|
||||
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||
}
|
||||
|
||||
get displayedDictionaryLabel() {
|
||||
@ -45,6 +46,10 @@ export class EditAnnotationDialogComponent
|
||||
return null;
|
||||
}
|
||||
|
||||
get showList() {
|
||||
return this.data.annotations.every(annotation => annotation.isSkipped || annotation.isRedacted);
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.#setTypes();
|
||||
}
|
||||
@ -66,9 +71,10 @@ export class EditAnnotationDialogComponent
|
||||
}
|
||||
|
||||
#getForm(): UntypedFormGroup {
|
||||
const sameType = new Set(this.data.annotations.map(annotation => annotation.type)).size === 1;
|
||||
return this._formBuilder.group({
|
||||
comment: [null],
|
||||
type: [this.data.annotations[0].type],
|
||||
type: [sameType ? this.data.annotations[0].type : null],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -114,9 +114,9 @@ export class AnnotationActionsService {
|
||||
requests.push(this._manualRedactionService.changeLegalBasis(changeLegalBasisBody, dossierId, fileId));
|
||||
}
|
||||
if (result.type !== annotations[0].type || this.#isDocumine) {
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(({ id }) => ({
|
||||
annotationId: id,
|
||||
type: result.type,
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => ({
|
||||
annotationId: annotation.id,
|
||||
type: result.type ?? annotation.type,
|
||||
}));
|
||||
requests.push(this._manualRedactionService.recategorizeRedactions(recategorizeBody, dossierId, fileId));
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { inject, Injectable, NgZone } from '@angular/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { BASE_HREF_FN } from '@iqser/common-ui/lib/utils';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
@ -21,7 +21,7 @@ export class PdfAnnotationActionsService {
|
||||
readonly #annotationActionsService = inject(AnnotationActionsService);
|
||||
readonly #iqserPermissionsService = inject(IqserPermissionsService);
|
||||
readonly #annotationManager = inject(REDAnnotationManager);
|
||||
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
get(annotations: AnnotationWrapper[]): IHeaderElement[] {
|
||||
const availableActions: IHeaderElement[] = [];
|
||||
const permissions = this.#getAnnotationsPermissions(annotations);
|
||||
@ -47,13 +47,13 @@ export class PdfAnnotationActionsService {
|
||||
|
||||
availableActions.push(resizeButton);
|
||||
}
|
||||
|
||||
if (
|
||||
const canEditRedactions =
|
||||
permissions.canChangeLegalBasis ||
|
||||
permissions.canRecategorizeAnnotation ||
|
||||
permissions.canForceHint ||
|
||||
permissions.canForceRedaction
|
||||
) {
|
||||
permissions.canForceRedaction;
|
||||
const canEdit = this.#isDocumine && annotations.length > 1 ? permissions.canEditAnnotations : canEditRedactions;
|
||||
if (canEdit) {
|
||||
const editButton = this.#getButton('edit', _('annotation-actions.edit-redaction.label'), () =>
|
||||
this.#annotationActionsService.editRedaction(annotations),
|
||||
);
|
||||
|
||||
@ -1235,9 +1235,10 @@
|
||||
}
|
||||
},
|
||||
"reason": "",
|
||||
"redacted-text": "Annotated text",
|
||||
"redacted-text": "Selected {length, plural, one{annotation} other {annotations}}",
|
||||
"section": "",
|
||||
"type": "Type"
|
||||
"type": "Type",
|
||||
"unchanged": "Unchanged"
|
||||
},
|
||||
"title": "Edit annotation"
|
||||
}
|
||||
|
||||
@ -39,6 +39,11 @@
|
||||
src: url('./assets/styles/fonts/Inter-VariableFont.ttf') format('truetype');
|
||||
}
|
||||
|
||||
.error-glyph-margin {
|
||||
background: url('./assets/icons/general/alert-circle.svg') no-repeat center;
|
||||
background-size: 80%;
|
||||
}
|
||||
|
||||
@include common-variables.configureLight(
|
||||
$iqser-primary: vars.$primary,
|
||||
$iqser-primary-rgb: common-functions.hexToRgb(vars.$primary),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user