Merge branch 'dan' into 'master'
Migrate to Entity Log See merge request redactmanager/red-ui!168
This commit is contained in:
commit
fc211daf25
@ -156,7 +156,7 @@ export const appModuleFactory = (config: AppConfig) => {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
REDACTION_LOG: {
|
REDACTION_LOG: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
},
|
},
|
||||||
VIEWED_PAGES: {
|
VIEWED_PAGES: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@ -2,8 +2,7 @@ import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
|||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary } from '@red/domain';
|
||||||
|
|
||||||
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) =>
|
export const canUndo = (annotation: AnnotationWrapper, isApprover: boolean) => !isApprover && annotation.pending;
|
||||||
!isApprover && (annotation.isSuggestion || annotation.pending);
|
|
||||||
|
|
||||||
export const canForceHint = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canForceHint = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||||
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
canAddRedaction && annotation.isIgnoredHint && !annotation.pending;
|
||||||
@ -33,15 +32,10 @@ export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedacti
|
|||||||
canAddRedaction && annotation.isRedacted && !annotation.pending;
|
canAddRedaction && annotation.isRedacted && !annotation.pending;
|
||||||
|
|
||||||
export const canRecategorizeAnnotation = (annotation: AnnotationWrapper, canRecategorize: boolean) =>
|
export const canRecategorizeAnnotation = (annotation: AnnotationWrapper, canRecategorize: boolean) =>
|
||||||
canRecategorize &&
|
canRecategorize && (annotation.isImage || annotation.isDictBasedHint) && !annotation.pending;
|
||||||
((annotation.isImage && !annotation.isSuggestion) || annotation.isSuggestionRecategorizeImage || annotation.hintDictionary) &&
|
|
||||||
!annotation.pending;
|
|
||||||
|
|
||||||
export const canResizeAnnotation = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
export const canResizeAnnotation = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||||
canAddRedaction &&
|
canAddRedaction &&
|
||||||
!annotation.isSkipped &&
|
!annotation.isSkipped &&
|
||||||
!annotation.pending &&
|
!annotation.pending &&
|
||||||
(((annotation.isRedacted || annotation.isImage) && !annotation.isSuggestion) ||
|
(annotation.isRedacted || annotation.isImage || annotation.isDictBasedHint || annotation.isRecommendation);
|
||||||
annotation.isSuggestionResize ||
|
|
||||||
annotation.isDictBasedHint ||
|
|
||||||
annotation.isRecommendation);
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { IqserPermissionsService } from '@iqser/common-ui';
|
|||||||
import { Dictionary } from '@red/domain';
|
import { Dictionary } from '@red/domain';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { isArray } from 'lodash-es';
|
import { isArray } from 'lodash-es';
|
||||||
|
import { IMAGE_CATEGORIES } from '../../modules/file-preview/utils/constants';
|
||||||
import {
|
import {
|
||||||
canAcceptRecommendation,
|
canAcceptRecommendation,
|
||||||
canChangeLegalBasis,
|
canChangeLegalBasis,
|
||||||
@ -16,7 +17,6 @@ import {
|
|||||||
canUndo,
|
canUndo,
|
||||||
} from './annotation-permissions.utils';
|
} from './annotation-permissions.utils';
|
||||||
import { AnnotationWrapper } from './annotation.wrapper';
|
import { AnnotationWrapper } from './annotation.wrapper';
|
||||||
import { IMAGE_CATEGORIES } from '../../modules/file-preview/utils/constants';
|
|
||||||
|
|
||||||
export class AnnotationPermissions {
|
export class AnnotationPermissions {
|
||||||
canUndo = true;
|
canUndo = true;
|
||||||
@ -63,8 +63,7 @@ export class AnnotationPermissions {
|
|||||||
permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction);
|
permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction);
|
||||||
|
|
||||||
permissions.canEditAnnotations = (annotation.isSkipped || annotation.isRedacted) && !annotation.isImage;
|
permissions.canEditAnnotations = (annotation.isSkipped || annotation.isRedacted) && !annotation.isImage;
|
||||||
permissions.canEditHints =
|
permissions.canEditHints = annotation.isIgnoredHint || annotation.isDictBasedHint || annotation.isHint;
|
||||||
annotation.isIgnoredHint || annotation.isDictBasedHint || annotation.isHint || annotation.isSuggestionForceHint;
|
|
||||||
permissions.canEditImages = [...IMAGE_CATEGORIES, 'ocr'].includes(annotation.type);
|
permissions.canEditImages = [...IMAGE_CATEGORIES, 'ocr'].includes(annotation.type);
|
||||||
|
|
||||||
summedPermissions._merge(permissions);
|
summedPermissions._merge(permissions);
|
||||||
|
|||||||
@ -1,88 +1,69 @@
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { IListable } from '@iqser/common-ui';
|
import { IListable } from '@iqser/common-ui';
|
||||||
import {
|
import {
|
||||||
annotationDefaultColorConfig,
|
|
||||||
annotationEntityColorConfig,
|
annotationEntityColorConfig,
|
||||||
AnnotationIconType,
|
AnnotationIconType,
|
||||||
ChangeType,
|
ChangeType,
|
||||||
ChangeTypes,
|
|
||||||
DefaultColors,
|
|
||||||
Dictionary,
|
Dictionary,
|
||||||
Earmark,
|
Earmark,
|
||||||
|
EntityTypes,
|
||||||
|
EntryStates,
|
||||||
FalsePositiveSuperTypes,
|
FalsePositiveSuperTypes,
|
||||||
IComment,
|
IEntityLogEntry,
|
||||||
ILegalBasis,
|
ILegalBasis,
|
||||||
IManualChange,
|
|
||||||
IPoint,
|
IPoint,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
IRedactionLogEntry,
|
|
||||||
LogEntryEngine,
|
LogEntryEngine,
|
||||||
LogEntryEngines,
|
LogEntryEngines,
|
||||||
LogEntryStatuses,
|
|
||||||
LowLevelFilterTypes,
|
LowLevelFilterTypes,
|
||||||
ManualRedactionTypes,
|
ManualRedactionTypes,
|
||||||
SuggestionAddSuperTypes,
|
|
||||||
SuggestionRemoveSuperTypes,
|
|
||||||
SuggestionsSuperTypes,
|
|
||||||
SuperType,
|
SuperType,
|
||||||
|
SuperTypeMapper,
|
||||||
SuperTypes,
|
SuperTypes,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { annotationTypesTranslations, SuggestionAddFalsePositive } from '@translations/annotation-types-translations';
|
import { annotationTypesTranslations } from '@translations/annotation-types-translations';
|
||||||
import { chronologicallyBy, timestampOf } from '../../modules/file-preview/services/file-data.service';
|
|
||||||
|
|
||||||
export class AnnotationWrapper implements IListable {
|
export class AnnotationWrapper implements IListable {
|
||||||
[x: string]: unknown;
|
|
||||||
|
|
||||||
superType: SuperType;
|
|
||||||
typeValue: string;
|
|
||||||
recategorizationType: string;
|
|
||||||
color: string;
|
|
||||||
entity: Dictionary;
|
|
||||||
comments: IComment[] = [];
|
|
||||||
firstTopLeftPoint: IPoint;
|
|
||||||
id: string;
|
id: string;
|
||||||
|
superType: SuperType;
|
||||||
|
superTypeLabel: string;
|
||||||
|
type: string;
|
||||||
|
typeLabel?: string;
|
||||||
|
color: string;
|
||||||
|
numberOfComments = 0;
|
||||||
|
firstTopLeftPoint: IPoint;
|
||||||
shortContent: string;
|
shortContent: string;
|
||||||
content: string;
|
content: string;
|
||||||
value: string;
|
value: string;
|
||||||
typeLabel: string;
|
|
||||||
pageNumber: number;
|
pageNumber: number;
|
||||||
hint: boolean;
|
dictionaryOperation = false;
|
||||||
redaction: boolean;
|
positions: IRectangle[] = [];
|
||||||
status: string;
|
|
||||||
dictionaryOperation: boolean;
|
|
||||||
positions: IRectangle[];
|
|
||||||
recommendationType: string;
|
|
||||||
legalBasisValue: string;
|
legalBasisValue: string;
|
||||||
legalBasisChangeValue?: string;
|
// AREA === rectangle
|
||||||
rectangle?: boolean;
|
AREA = false;
|
||||||
|
HINT = false;
|
||||||
|
IMAGE = false;
|
||||||
section?: string;
|
section?: string;
|
||||||
reference: string[];
|
reference: string[] = [];
|
||||||
imported?: boolean;
|
imported = false;
|
||||||
image?: boolean;
|
manual = false;
|
||||||
manual?: boolean;
|
|
||||||
pending = false;
|
pending = false;
|
||||||
hintDictionary = false;
|
|
||||||
textAfter?: string;
|
textAfter?: string;
|
||||||
textBefore?: string;
|
textBefore?: string;
|
||||||
isChangeLogEntry = false;
|
isChangeLogEntry = false;
|
||||||
changeLogType?: 'ADDED' | 'REMOVED' | 'CHANGED';
|
engines: LogEntryEngine[] = [];
|
||||||
engines?: LogEntryEngine[];
|
|
||||||
hasBeenResized: boolean;
|
hasBeenResized: boolean;
|
||||||
hasBeenRecategorized: boolean;
|
hasBeenRecategorized: boolean;
|
||||||
hasLegalBasisChanged: boolean;
|
hasLegalBasisChanged: boolean;
|
||||||
hasBeenForcedHint: boolean;
|
hasBeenForcedHint: boolean;
|
||||||
hasBeenForcedRedaction: boolean;
|
hasBeenForcedRedaction: boolean;
|
||||||
hasBeenRemovedByManualOverride: boolean;
|
hasBeenRemovedByManualOverride: boolean;
|
||||||
legalBasisList: ILegalBasis[] = [];
|
isIgnored = false;
|
||||||
|
|
||||||
get searchKey(): string {
|
get searchKey(): string {
|
||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isChangeLogRemoved() {
|
|
||||||
return this.changeLogType === 'REMOVED';
|
|
||||||
}
|
|
||||||
|
|
||||||
get descriptor() {
|
get descriptor() {
|
||||||
return this.isModifyDictionary ? _('dictionary') : _('type');
|
return this.isModifyDictionary ? _('dictionary') : _('type');
|
||||||
}
|
}
|
||||||
@ -105,30 +86,18 @@ export class AnnotationWrapper implements IListable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuperTypeBasedColor() {
|
|
||||||
return this.isSuggestion || this.isDeclinedSuggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSkipped() {
|
get isSkipped() {
|
||||||
return this.superType === SuperTypes.Skipped;
|
return this.superType === SuperTypes.Skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isImage() {
|
get isImage() {
|
||||||
return this.type?.toLowerCase() === 'image' || this.image;
|
return this.type?.toLowerCase() === 'image' || this.IMAGE;
|
||||||
}
|
|
||||||
|
|
||||||
get isNotSignatureImage() {
|
|
||||||
return this.isImage && this.recategorizationType === 'signature';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOCR() {
|
get isOCR() {
|
||||||
return this.type?.toLowerCase() === 'ocr';
|
return this.type?.toLowerCase() === 'ocr';
|
||||||
}
|
}
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this.recategorizationType || this.typeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
get topLevelFilter() {
|
get topLevelFilter() {
|
||||||
return !LowLevelFilterTypes[this.superType];
|
return !LowLevelFilterTypes[this.superType];
|
||||||
}
|
}
|
||||||
@ -145,18 +114,6 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
|
return this.type?.toLowerCase() === 'false_positive' && !!FalsePositiveSuperTypes[this.superType];
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestionAddToFalsePositive() {
|
|
||||||
return this.typeLabel === annotationTypesTranslations[SuggestionAddFalsePositive];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isDeclinedSuggestion() {
|
|
||||||
return this.superType === SuperTypes.DeclinedSuggestion;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isApproved() {
|
|
||||||
return this.status === 'APPROVED';
|
|
||||||
}
|
|
||||||
|
|
||||||
get isHint() {
|
get isHint() {
|
||||||
return this.superType === SuperTypes.Hint;
|
return this.superType === SuperTypes.Hint;
|
||||||
}
|
}
|
||||||
@ -170,14 +127,10 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return 'hexagon';
|
return 'hexagon';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isHint || this.isIgnoredHint) {
|
if (this.HINT) {
|
||||||
return 'circle';
|
return 'circle';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isSuggestion || this.isDeclinedSuggestion) {
|
|
||||||
return 'rhombus';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'square';
|
return 'square';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,42 +146,6 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
|
return this.superType === SuperTypes.Redaction || this.superType === SuperTypes.ManualRedaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSuggestion() {
|
|
||||||
return !!SuggestionsSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionResize() {
|
|
||||||
return this.superType === SuperTypes.SuggestionResize;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRecategorizeImage() {
|
|
||||||
return this.superType === SuperTypes.SuggestionRecategorizeImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionForceHint() {
|
|
||||||
return this.superType === SuperTypes.SuggestionForceHint;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionAdd() {
|
|
||||||
return !!SuggestionAddSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionAddDictionary() {
|
|
||||||
return this.superType === SuperTypes.SuggestionAddDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRemove() {
|
|
||||||
return !!SuggestionRemoveSuperTypes[this.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionRemoveDictionary() {
|
|
||||||
return this.superType === SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isSuggestionLegalBasisChange() {
|
|
||||||
return this.superType === SuperTypes.SuggestionChangeLegalBasis;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isModifyDictionary() {
|
get isModifyDictionary() {
|
||||||
return this.dictionaryOperation;
|
return this.dictionaryOperation;
|
||||||
}
|
}
|
||||||
@ -257,7 +174,7 @@ export class AnnotationWrapper implements IListable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get legalBasis() {
|
get legalBasis() {
|
||||||
return this.legalBasisChangeValue || this.legalBasisValue;
|
return this.legalBasisValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
get width(): number {
|
get width(): number {
|
||||||
@ -268,172 +185,107 @@ export class AnnotationWrapper implements IListable {
|
|||||||
return Math.floor(this.positions[0].height);
|
return Math.floor(this.positions[0].height);
|
||||||
}
|
}
|
||||||
|
|
||||||
get previewAnnotation() {
|
|
||||||
return (
|
|
||||||
this.isRedacted ||
|
|
||||||
this.isSuggestionAdd ||
|
|
||||||
this.isSuggestionRemove ||
|
|
||||||
this.isSuggestionResize ||
|
|
||||||
this.isSuggestionLegalBasisChange ||
|
|
||||||
this.isSuggestionRecategorizeImage
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromEarmark(earmark: Earmark) {
|
static fromEarmark(earmark: Earmark) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.id = earmark.id;
|
annotationWrapper.id = earmark.id;
|
||||||
annotationWrapper.pageNumber = earmark.positions[0].page;
|
annotationWrapper.pageNumber = earmark.positions[0].page;
|
||||||
annotationWrapper.superType = SuperTypes.TextHighlight;
|
annotationWrapper.superType = SuperTypes.TextHighlight;
|
||||||
annotationWrapper.typeValue = SuperTypes.TextHighlight;
|
annotationWrapper.type = SuperTypes.TextHighlight;
|
||||||
annotationWrapper.value = 'Imported';
|
annotationWrapper.value = 'Imported';
|
||||||
annotationWrapper.color = earmark.hexColor;
|
annotationWrapper.color = earmark.hexColor;
|
||||||
annotationWrapper.positions = earmark.positions;
|
annotationWrapper.positions = earmark.positions;
|
||||||
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
|
annotationWrapper.firstTopLeftPoint = earmark.positions[0]?.topLeft;
|
||||||
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromData(
|
static fromData(
|
||||||
redactionLogEntry: IRedactionLogEntry,
|
logEntry: IEntityLogEntry,
|
||||||
dictionaries: Dictionary[],
|
dictionary: Dictionary,
|
||||||
defaultColors: DefaultColors,
|
|
||||||
changeLogType: ChangeType,
|
changeLogType: ChangeType,
|
||||||
legalBasisList: ILegalBasis[],
|
legalBasisList: ILegalBasis[],
|
||||||
hintDictionary: boolean,
|
|
||||||
isDocumine: boolean,
|
isDocumine: boolean,
|
||||||
) {
|
) {
|
||||||
const annotationWrapper = new AnnotationWrapper();
|
const annotationWrapper = new AnnotationWrapper();
|
||||||
|
|
||||||
annotationWrapper.id = redactionLogEntry.id;
|
annotationWrapper.id = logEntry.id;
|
||||||
annotationWrapper.isChangeLogEntry = !!changeLogType;
|
annotationWrapper.isChangeLogEntry = !!changeLogType;
|
||||||
annotationWrapper.changeLogType = changeLogType;
|
annotationWrapper.type = logEntry.type;
|
||||||
annotationWrapper.legalBasisList = legalBasisList;
|
annotationWrapper.value = logEntry.value;
|
||||||
annotationWrapper.redaction = redactionLogEntry.redacted;
|
annotationWrapper.firstTopLeftPoint = { x: logEntry.positions[0].rectangle[0], y: logEntry.positions[0].rectangle[1] };
|
||||||
annotationWrapper.hint = redactionLogEntry.hint;
|
annotationWrapper.pageNumber = logEntry.positions[0].pageNumber;
|
||||||
annotationWrapper.typeValue = redactionLogEntry.type;
|
annotationWrapper.positions = logEntry.positions.map(p => ({
|
||||||
annotationWrapper.value = redactionLogEntry.value;
|
page: p.pageNumber,
|
||||||
annotationWrapper.firstTopLeftPoint = redactionLogEntry.positions[0]?.topLeft;
|
height: p.rectangle[3],
|
||||||
annotationWrapper.pageNumber = redactionLogEntry.positions[0]?.page;
|
width: p.rectangle[2],
|
||||||
annotationWrapper.positions = redactionLogEntry.positions;
|
topLeft: { x: p.rectangle[0], y: p.rectangle[1] },
|
||||||
annotationWrapper.textBefore = redactionLogEntry.textBefore;
|
}));
|
||||||
annotationWrapper.textAfter = redactionLogEntry.textAfter;
|
annotationWrapper.textBefore = logEntry.textBefore;
|
||||||
annotationWrapper.dictionaryOperation = redactionLogEntry.dictionaryEntry;
|
annotationWrapper.textAfter = logEntry.textAfter;
|
||||||
annotationWrapper.image = redactionLogEntry.image;
|
annotationWrapper.dictionaryOperation = logEntry.dictionaryEntry;
|
||||||
annotationWrapper.imported = redactionLogEntry.imported;
|
|
||||||
annotationWrapper.legalBasisValue = redactionLogEntry.legalBasis;
|
|
||||||
annotationWrapper.comments = redactionLogEntry.comments || [];
|
|
||||||
annotationWrapper.manual = redactionLogEntry.manualChanges?.length > 0;
|
|
||||||
annotationWrapper.engines = redactionLogEntry.engines;
|
|
||||||
annotationWrapper.section = redactionLogEntry.section;
|
|
||||||
annotationWrapper.reference = redactionLogEntry.reference || [];
|
|
||||||
annotationWrapper.rectangle = redactionLogEntry.rectangle;
|
|
||||||
annotationWrapper.hintDictionary = hintDictionary;
|
|
||||||
annotationWrapper.hasBeenResized = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RESIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenRecategorized = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasLegalBasisChanged = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenForcedHint = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenForcedRedaction = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.hasBeenRemovedByManualOverride = !!redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY && c.annotationStatus === LogEntryStatuses.APPROVED,
|
|
||||||
);
|
|
||||||
annotationWrapper.legalBasisChangeValue = redactionLogEntry.manualChanges?.find(
|
|
||||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
|
|
||||||
)?.propertyChanges.legalBasis;
|
|
||||||
|
|
||||||
this.#createContent(annotationWrapper, redactionLogEntry, isDocumine);
|
annotationWrapper.HINT = logEntry.entryType === EntityTypes.HINT;
|
||||||
this.#setSuperType(annotationWrapper, redactionLogEntry);
|
annotationWrapper.IMAGE = logEntry.entryType === EntityTypes.IMAGE;
|
||||||
this.#handleRecommendations(annotationWrapper, redactionLogEntry);
|
annotationWrapper.AREA = logEntry.entryType === EntityTypes.AREA;
|
||||||
annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);
|
|
||||||
|
|
||||||
const entity = dictionaries.find(d => d.type === annotationWrapper.typeValue);
|
annotationWrapper.isIgnored = logEntry.state === EntryStates.IGNORED;
|
||||||
annotationWrapper.entity = entity?.virtual ? null : entity;
|
|
||||||
|
|
||||||
let colorKey = annotationWrapper.isSuperTypeBasedColor
|
annotationWrapper.numberOfComments = logEntry.numberOfComments;
|
||||||
? annotationDefaultColorConfig[annotationWrapper.superType]
|
annotationWrapper.imported = logEntry.imported;
|
||||||
: annotationEntityColorConfig[annotationWrapper.superType];
|
annotationWrapper.legalBasisValue = logEntry.legalBasis;
|
||||||
|
annotationWrapper.manual = logEntry.manualChanges?.length > 0;
|
||||||
|
annotationWrapper.engines = logEntry.engines ?? [];
|
||||||
|
annotationWrapper.section = logEntry.section;
|
||||||
|
annotationWrapper.reference = logEntry.reference || [];
|
||||||
|
annotationWrapper.hasBeenResized = !!logEntry.manualChanges?.find(c => c.manualRedactionType === ManualRedactionTypes.RESIZE);
|
||||||
|
annotationWrapper.hasBeenRecategorized = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.RECATEGORIZE,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasLegalBasisChanged = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasBeenForcedHint = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_HINT,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasBeenForcedRedaction = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.FORCE_REDACT,
|
||||||
|
);
|
||||||
|
annotationWrapper.hasBeenRemovedByManualOverride = !!logEntry.manualChanges?.find(
|
||||||
|
c => c.manualRedactionType === ManualRedactionTypes.REMOVE_LOCALLY,
|
||||||
|
);
|
||||||
|
|
||||||
if (annotationWrapper.isSuperTypeBasedColor && annotationWrapper.isSuggestionResize && !annotationWrapper.isModifyDictionary) {
|
const content = this.#createContent(annotationWrapper, logEntry, isDocumine);
|
||||||
colorKey = annotationDefaultColorConfig[SuperTypes.SuggestionAdd];
|
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper, legalBasisList) || content;
|
||||||
}
|
annotationWrapper.content = content;
|
||||||
|
|
||||||
annotationWrapper.color = annotationWrapper.isSuperTypeBasedColor ? defaultColors[colorKey] : (entity[colorKey] as string);
|
const lastRelevantManualChange = logEntry.manualChanges?.at(-1);
|
||||||
|
annotationWrapper.pending = lastRelevantManualChange && !lastRelevantManualChange.processed;
|
||||||
|
annotationWrapper.superType = SuperTypeMapper[logEntry.entryType][logEntry.state](logEntry);
|
||||||
|
annotationWrapper.superTypeLabel = annotationTypesTranslations[annotationWrapper.superType];
|
||||||
|
|
||||||
|
annotationWrapper.typeLabel = dictionary.virtual ? undefined : dictionary.label;
|
||||||
|
|
||||||
|
const colorKey = annotationEntityColorConfig[annotationWrapper.superType];
|
||||||
|
annotationWrapper.color = dictionary[colorKey] as string;
|
||||||
|
annotationWrapper['entry'] = logEntry;
|
||||||
|
|
||||||
return annotationWrapper;
|
return annotationWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getTypeLabel(redactionLogEntry: IRedactionLogEntry, annotation: AnnotationWrapper): string {
|
static #createContent(annotationWrapper: AnnotationWrapper, logEntry: IEntityLogEntry, isDocumine: boolean) {
|
||||||
if (redactionLogEntry.reason?.toLowerCase() === 'false positive') {
|
|
||||||
return annotationTypesTranslations[SuggestionAddFalsePositive];
|
|
||||||
}
|
|
||||||
if (annotation.superType === SuperTypes.ManualRedaction && annotation.hintDictionary) {
|
|
||||||
return _('annotation-type.manual-hint');
|
|
||||||
}
|
|
||||||
return annotationTypesTranslations[annotation.superType];
|
|
||||||
}
|
|
||||||
|
|
||||||
static #handleRecommendations(annotationWrapper: AnnotationWrapper, redactionLogEntry: IRedactionLogEntry) {
|
|
||||||
if (annotationWrapper.superType === SuperTypes.Recommendation) {
|
|
||||||
annotationWrapper.recommendationType = redactionLogEntry.type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static #setSuperType(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry) {
|
|
||||||
if (entry.manualChanges?.length) {
|
|
||||||
const lastRelevantManualChange = entry.manualChanges?.at(-1);
|
|
||||||
const viableChanges = entry.changes.filter(c => c.analysisNumber > 1);
|
|
||||||
const lastChange = viableChanges.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
|
||||||
const lastChangeOccurredAfterLastManualChange =
|
|
||||||
lastChange && timestampOf(lastChange.dateTime) > timestampOf(lastRelevantManualChange.processedDate);
|
|
||||||
|
|
||||||
if (lastChangeOccurredAfterLastManualChange && lastChange.type === ChangeTypes.ADDED && entry.redacted) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
annotationWrapper.pending = !lastRelevantManualChange.processed;
|
|
||||||
|
|
||||||
annotationWrapper.superType = this.#selectSuperType(entry, lastRelevantManualChange, annotationWrapper.hintDictionary);
|
|
||||||
|
|
||||||
if (lastRelevantManualChange.annotationStatus === LogEntryStatuses.REQUESTED) {
|
|
||||||
annotationWrapper.recategorizationType = lastRelevantManualChange.propertyChanges.type;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (entry.recommendation) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Recommendation;
|
|
||||||
} else if (entry.redacted) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Redaction;
|
|
||||||
} else if (entry.hint) {
|
|
||||||
annotationWrapper.superType = SuperTypes.Hint;
|
|
||||||
} else {
|
|
||||||
annotationWrapper.superType = SuperTypes.Skipped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry, isDocumine: boolean) {
|
|
||||||
let content = '';
|
let content = '';
|
||||||
if (entry.matchedRule) {
|
if (logEntry.matchedRule) {
|
||||||
content += `Rule ${entry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
content += `Rule ${logEntry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.reason) {
|
if (logEntry.reason) {
|
||||||
if (isDocumine && entry.reason.slice(-1) === '.') {
|
if (isDocumine && logEntry.reason.slice(-1) === '.') {
|
||||||
entry.reason = entry.reason.slice(0, -1);
|
logEntry.reason = logEntry.reason.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
content += entry.reason + '\n\n';
|
content += logEntry.reason + '\n\n';
|
||||||
//remove leading and trailing commas and whitespaces
|
//remove leading and trailing commas and whitespaces
|
||||||
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||||
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
||||||
@ -447,23 +299,20 @@ export class AnnotationWrapper implements IListable {
|
|||||||
content += 'Removed by manual override';
|
content += 'Removed by manual override';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.section) {
|
if (logEntry.section) {
|
||||||
let prefix = `In section${isDocumine ? '' : ':'} `;
|
let prefix = `In section${isDocumine ? '' : ':'} `;
|
||||||
if (content.length) {
|
if (content.length) {
|
||||||
prefix = ` ${prefix.toLowerCase()}`;
|
prefix = ` ${prefix.toLowerCase()}`;
|
||||||
}
|
}
|
||||||
content += `${prefix} "${entry.section}"`;
|
content += `${prefix} "${logEntry.section}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
annotationWrapper.shortContent = this.#getShortContent(annotationWrapper) || content;
|
return content;
|
||||||
annotationWrapper.content = content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static #getShortContent(annotationWrapper: AnnotationWrapper) {
|
static #getShortContent(annotationWrapper: AnnotationWrapper, legalBasisList: ILegalBasis[]) {
|
||||||
if (annotationWrapper.legalBasis) {
|
if (annotationWrapper.legalBasis) {
|
||||||
const lb = annotationWrapper.legalBasisList.find(
|
const lb = legalBasisList.find(lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()));
|
||||||
lbm => lbm.reason?.toLowerCase().includes(annotationWrapper.legalBasis.toLowerCase()),
|
|
||||||
);
|
|
||||||
if (lb) {
|
if (lb) {
|
||||||
return lb.name;
|
return lb.name;
|
||||||
}
|
}
|
||||||
@ -471,154 +320,4 @@ export class AnnotationWrapper implements IListable {
|
|||||||
|
|
||||||
return annotationWrapper.legalBasis;
|
return annotationWrapper.legalBasis;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #selectSuperType(redactionLogEntry: IRedactionLogEntry, lastManualChange: IManualChange, isHintDictionary: boolean): SuperType {
|
|
||||||
switch (lastManualChange.manualRedactionType) {
|
|
||||||
case ManualRedactionTypes.ADD_LOCALLY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.ManualRedaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.DeclinedSuggestion;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionAdd;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.ADD_TO_DICTIONARY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.DeclinedSuggestion;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionAddDictionary;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.REMOVE_LOCALLY:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED: {
|
|
||||||
if (isHintDictionary) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (redactionLogEntry.redacted) {
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SuperTypes.Skipped;
|
|
||||||
}
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemove;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.REMOVE_FROM_DICTIONARY:
|
|
||||||
if (redactionLogEntry.redacted) {
|
|
||||||
if (lastManualChange.processed) {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (lastManualChange.processed) {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return redactionLogEntry.recommendation ? SuperTypes.Recommendation : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.Hint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRemoveDictionary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.FORCE_REDACT:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionForceRedaction;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.FORCE_HINT:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return SuperTypes.IgnoredHint;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionForceHint;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.RECATEGORIZE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED: {
|
|
||||||
if (redactionLogEntry.recommendation) {
|
|
||||||
return SuperTypes.Recommendation;
|
|
||||||
} else if (redactionLogEntry.redacted) {
|
|
||||||
return SuperTypes.Redaction;
|
|
||||||
} else if (redactionLogEntry.hint) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
}
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionRecategorizeImage;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.LEGAL_BASIS_CHANGE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction ? SuperTypes.ManualRedaction : SuperTypes.Redaction;
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionChangeLegalBasis;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ManualRedactionTypes.RESIZE:
|
|
||||||
switch (lastManualChange.annotationStatus) {
|
|
||||||
case LogEntryStatuses.APPROVED:
|
|
||||||
case LogEntryStatuses.DECLINED:
|
|
||||||
if (redactionLogEntry.recommendation) {
|
|
||||||
return SuperTypes.Recommendation;
|
|
||||||
} else if (redactionLogEntry.redacted) {
|
|
||||||
return redactionLogEntry.type === SuperTypes.ManualRedaction
|
|
||||||
? SuperTypes.ManualRedaction
|
|
||||||
: SuperTypes.Redaction;
|
|
||||||
} else if (redactionLogEntry.hint) {
|
|
||||||
return SuperTypes.Hint;
|
|
||||||
}
|
|
||||||
return isHintDictionary ? SuperTypes.IgnoredHint : SuperTypes.Skipped;
|
|
||||||
|
|
||||||
case LogEntryStatuses.REQUESTED:
|
|
||||||
return SuperTypes.SuggestionResize;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,12 +14,6 @@
|
|||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
<redaction-annotation-icon *ngIf="file.hasImages" [color]="imageColor$ | async" label="I" type="square"></redaction-annotation-icon>
|
<redaction-annotation-icon *ngIf="file.hasImages" [color]="imageColor$ | async" label="I" type="square"></redaction-annotation-icon>
|
||||||
<redaction-annotation-icon *ngIf="file.hintsOnly" [color]="hintColor$ | async" label="H" type="circle"></redaction-annotation-icon>
|
<redaction-annotation-icon *ngIf="file.hintsOnly" [color]="hintColor$ | async" label="H" type="circle"></redaction-annotation-icon>
|
||||||
<redaction-annotation-icon
|
|
||||||
*ngIf="file.hasSuggestions"
|
|
||||||
[color]="suggestionColor$ | async"
|
|
||||||
label="S"
|
|
||||||
type="rhombus"
|
|
||||||
></redaction-annotation-icon>
|
|
||||||
<mat-icon *ngIf="file.hasAnnotationComments" svgIcon="red:comment"></mat-icon>
|
<mat-icon *ngIf="file.hasAnnotationComments" svgIcon="red:comment"></mat-icon>
|
||||||
<ng-container *ngIf="noWorkloadItems"> - </ng-container>
|
<ng-container *ngIf="noWorkloadItems"> -</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { annotationDefaultColorConfig, DefaultBasedColorType, File } from '@red/domain';
|
import { annotationDefaultColorConfig, DefaultBasedColorType, File } from '@red/domain';
|
||||||
import { UserService } from '@users/user.service';
|
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
|
import { UserService } from '@users/user.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -14,17 +14,14 @@ import { map } from 'rxjs/operators';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class FileWorkloadComponent implements OnInit {
|
export class FileWorkloadComponent implements OnInit {
|
||||||
|
#dossierTemplateId: string;
|
||||||
@Input() file: File;
|
@Input() file: File;
|
||||||
|
|
||||||
suggestionColor$: Observable<string>;
|
|
||||||
imageColor$: Observable<string>;
|
imageColor$: Observable<string>;
|
||||||
updatedColor$: Observable<string>;
|
updatedColor$: Observable<string>;
|
||||||
analysisColor$: Observable<string>;
|
analysisColor$: Observable<string>;
|
||||||
hintColor$: Observable<string>;
|
hintColor$: Observable<string>;
|
||||||
redactionColor$: Observable<string>;
|
redactionColor$: Observable<string>;
|
||||||
|
|
||||||
#dossierTemplateId: string;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
private readonly _defaultColorsService: DefaultColorsService,
|
private readonly _defaultColorsService: DefaultColorsService,
|
||||||
@ -32,20 +29,6 @@ export class FileWorkloadComponent implements OnInit {
|
|||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
private readonly _dictionariesMapService: DictionariesMapService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.#dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
|
|
||||||
this.suggestionColor$ = this.#getDefaultColor$('suggestion');
|
|
||||||
this.updatedColor$ = this.#getDefaultColor$('updated');
|
|
||||||
this.analysisColor$ = this.#getDefaultColor$('analysis');
|
|
||||||
this.hintColor$ = this.#getDefaultColor$('hint');
|
|
||||||
this.redactionColor$ = this.#getDefaultColor$('redaction');
|
|
||||||
this.imageColor$ = this._dictionariesMapService.watch$(this.#dossierTemplateId, 'image').pipe(map(e => e.hexColor));
|
|
||||||
}
|
|
||||||
|
|
||||||
#getDefaultColor$(type: DefaultBasedColorType): Observable<string> {
|
|
||||||
return this._defaultColorsService.getColor$(this.#dossierTemplateId, annotationDefaultColorConfig[type]);
|
|
||||||
}
|
|
||||||
|
|
||||||
get updated(): boolean {
|
get updated(): boolean {
|
||||||
return this.file.hasUpdates && this.file.assignee === this._userService.currentUser.id && !this.file.isApproved;
|
return this.file.hasUpdates && this.file.assignee === this._userService.currentUser.id && !this.file.isApproved;
|
||||||
}
|
}
|
||||||
@ -57,8 +40,20 @@ export class FileWorkloadComponent implements OnInit {
|
|||||||
!this.file.hasRedactions &&
|
!this.file.hasRedactions &&
|
||||||
!this.file.hasImages &&
|
!this.file.hasImages &&
|
||||||
!this.file.hintsOnly &&
|
!this.file.hintsOnly &&
|
||||||
!this.file.hasSuggestions &&
|
|
||||||
!this.file.hasAnnotationComments
|
!this.file.hasAnnotationComments
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.#dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
|
||||||
|
this.updatedColor$ = this.#getDefaultColor$('updated');
|
||||||
|
this.analysisColor$ = this.#getDefaultColor$('analysis');
|
||||||
|
this.hintColor$ = this.#getDefaultColor$('hint');
|
||||||
|
this.redactionColor$ = this.#getDefaultColor$('redaction');
|
||||||
|
this.imageColor$ = this._dictionariesMapService.watch$(this.#dossierTemplateId, 'image').pipe(map(e => e.hexColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
#getDefaultColor$(type: DefaultBasedColorType): Observable<string> {
|
||||||
|
return this._defaultColorsService.getColor$(this.#dossierTemplateId, annotationDefaultColorConfig[type]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { Injectable, TemplateRef } from '@angular/core';
|
import { Injectable, TemplateRef } from '@angular/core';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import {
|
import {
|
||||||
ActionConfig,
|
ActionConfig,
|
||||||
getConfig,
|
getConfig,
|
||||||
@ -9,6 +10,10 @@ import {
|
|||||||
WorkflowColumn,
|
WorkflowColumn,
|
||||||
WorkflowConfig,
|
WorkflowConfig,
|
||||||
} from '@iqser/common-ui';
|
} from '@iqser/common-ui';
|
||||||
|
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
|
import { getParam, List } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
annotationDefaultColorConfig,
|
annotationDefaultColorConfig,
|
||||||
AnnotationShapeMap,
|
AnnotationShapeMap,
|
||||||
@ -22,26 +27,21 @@ import {
|
|||||||
WorkflowFileStatus,
|
WorkflowFileStatus,
|
||||||
WorkflowFileStatuses,
|
WorkflowFileStatuses,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { UserService } from '@users/user.service';
|
|
||||||
import { DossiersDialogService } from '../shared-dossiers/services/dossiers-dialog.service';
|
|
||||||
import { annotationFilterChecker, RedactionFilterSorter } from '../../utils';
|
|
||||||
import { workloadTranslations } from '@translations/workload-translations';
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
|
||||||
import { BulkActionsService } from './services/bulk-actions.service';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
|
||||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||||
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||||
|
import { workloadTranslations } from '@translations/workload-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
import { UserService } from '@users/user.service';
|
||||||
import { getParam, List } from '@iqser/common-ui/lib/utils';
|
import dayjs from 'dayjs';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
import { annotationFilterChecker, RedactionFilterSorter } from '../../utils';
|
||||||
|
import { DossiersDialogService } from '../shared-dossiers/services/dossiers-dialog.service';
|
||||||
|
import { BulkActionsService } from './services/bulk-actions.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConfigService {
|
export class ConfigService {
|
||||||
@ -198,9 +198,6 @@ export class ConfigService {
|
|||||||
if (file.hasRedactions) {
|
if (file.hasRedactions) {
|
||||||
allDistinctNeedsWork.add('redaction');
|
allDistinctNeedsWork.add('redaction');
|
||||||
}
|
}
|
||||||
if (file.hasSuggestions) {
|
|
||||||
allDistinctNeedsWork.add('suggestion');
|
|
||||||
}
|
|
||||||
if (file.hasUpdates && file.assignee === this._userService.currentUser.id && !file.isApproved) {
|
if (file.hasUpdates && file.assignee === this._userService.currentUser.id && !file.isApproved) {
|
||||||
allDistinctNeedsWork.add('updated');
|
allDistinctNeedsWork.add('updated');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,4 @@
|
|||||||
label="H"
|
label="H"
|
||||||
type="circle"
|
type="circle"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
|
||||||
<redaction-annotation-icon
|
|
||||||
*ngIf="dossierStats.hasSuggestionsFilePresent"
|
|
||||||
[color]="suggestionColor$ | async"
|
|
||||||
label="S"
|
|
||||||
type="rhombus"
|
|
||||||
></redaction-annotation-icon>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,19 +10,13 @@ import { BehaviorSubject, Observable, switchMap } from 'rxjs';
|
|||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class DossierWorkloadColumnComponent implements OnChanges {
|
export class DossierWorkloadColumnComponent implements OnChanges {
|
||||||
|
readonly #dossierTemplateId$ = new BehaviorSubject<string>(null);
|
||||||
@Input() dossier: Dossier;
|
@Input() dossier: Dossier;
|
||||||
@Input() dossierStats: DossierStats;
|
@Input() dossierStats: DossierStats;
|
||||||
|
|
||||||
readonly suggestionColor$: Observable<string>;
|
|
||||||
readonly hintColor$: Observable<string>;
|
readonly hintColor$: Observable<string>;
|
||||||
readonly redactionColor$: Observable<string>;
|
readonly redactionColor$: Observable<string>;
|
||||||
|
|
||||||
readonly #dossierTemplateId$ = new BehaviorSubject<string>(null);
|
|
||||||
|
|
||||||
constructor(private readonly _defaultColorsService: DefaultColorsService) {
|
constructor(private readonly _defaultColorsService: DefaultColorsService) {
|
||||||
this.suggestionColor$ = this.#dossierTemplateId$.pipe(
|
|
||||||
switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'requestAddColor')),
|
|
||||||
);
|
|
||||||
this.hintColor$ = this.#dossierTemplateId$.pipe(switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'hintColor')));
|
this.hintColor$ = this.#dossierTemplateId$.pipe(switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'hintColor')));
|
||||||
this.redactionColor$ = this.#dossierTemplateId$.pipe(
|
this.redactionColor$ = this.#dossierTemplateId$.pipe(
|
||||||
switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'redactionColor')),
|
switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'redactionColor')),
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
import { Injectable, TemplateRef } from '@angular/core';
|
import { Injectable, TemplateRef } from '@angular/core';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { ButtonConfig, getConfig, TableColumnConfig } from '@iqser/common-ui';
|
import { ButtonConfig, getConfig, TableColumnConfig } from '@iqser/common-ui';
|
||||||
|
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import {
|
import {
|
||||||
annotationDefaultColorConfig,
|
annotationDefaultColorConfig,
|
||||||
AnnotationShapeMap,
|
AnnotationShapeMap,
|
||||||
@ -9,11 +12,15 @@ import {
|
|||||||
User,
|
User,
|
||||||
WorkflowFileStatus,
|
WorkflowFileStatus,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
|
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
||||||
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { SharedDialogService } from '@shared/services/dialog.service';
|
||||||
|
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
||||||
|
import { workloadTranslations } from '@translations/workload-translations';
|
||||||
import { UserPreferenceService } from '@users/user-preference.service';
|
import { UserPreferenceService } from '@users/user-preference.service';
|
||||||
import { UserService } from '@users/user.service';
|
import { UserService } from '@users/user.service';
|
||||||
import { workflowFileStatusTranslations } from '@translations/file-status-translations';
|
|
||||||
import {
|
import {
|
||||||
dossierMemberChecker,
|
dossierMemberChecker,
|
||||||
dossierMemberQuickChecker,
|
dossierMemberQuickChecker,
|
||||||
@ -21,17 +28,11 @@ import {
|
|||||||
dossierStateChecker,
|
dossierStateChecker,
|
||||||
RedactionFilterSorter,
|
RedactionFilterSorter,
|
||||||
} from '../../utils';
|
} from '../../utils';
|
||||||
import { workloadTranslations } from '@translations/workload-translations';
|
|
||||||
import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
|
|
||||||
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { SharedDialogService } from '@shared/services/dialog.service';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
|
||||||
import { IFilterGroup, INestedFilter, keyChecker, NestedFilter } from '@iqser/common-ui/lib/filtering';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ConfigService {
|
export class ConfigService {
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
@ -106,9 +107,6 @@ export class ConfigService {
|
|||||||
if (stats.hasRedactionsFilePresent) {
|
if (stats.hasRedactionsFilePresent) {
|
||||||
allDistinctNeedsWork.add('redaction');
|
allDistinctNeedsWork.add('redaction');
|
||||||
}
|
}
|
||||||
if (stats.hasSuggestionsFilePresent) {
|
|
||||||
allDistinctNeedsWork.add('suggestion');
|
|
||||||
}
|
|
||||||
if (stats.hasNoFlagsFilePresent) {
|
if (stats.hasNoFlagsFilePresent) {
|
||||||
allDistinctNeedsWork.add('none');
|
allDistinctNeedsWork.add('none');
|
||||||
}
|
}
|
||||||
@ -246,12 +244,6 @@ export class ConfigService {
|
|||||||
private _annotationFilterChecker = (dossier: Dossier, filter: INestedFilter) => {
|
private _annotationFilterChecker = (dossier: Dossier, filter: INestedFilter) => {
|
||||||
const stats = this._dossierStatsService.get(dossier.id);
|
const stats = this._dossierStatsService.get(dossier.id);
|
||||||
switch (filter.id) {
|
switch (filter.id) {
|
||||||
// case 'analysis': {
|
|
||||||
// return stats.reanalysisRequired;
|
|
||||||
// }
|
|
||||||
case 'suggestion': {
|
|
||||||
return stats.hasSuggestionsFilePresent;
|
|
||||||
}
|
|
||||||
case 'redaction': {
|
case 'redaction': {
|
||||||
return stats.hasRedactionsFilePresent;
|
return stats.hasRedactionsFilePresent;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,10 @@
|
|||||||
<ng-container *ngIf="resizing && annotationPermissions.canResizeAnnotation">
|
<ng-container *ngIf="resizing && annotationPermissions.canResizeAnnotation">
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="acceptResize()"
|
(action)="acceptResize()"
|
||||||
|
[class.disabled]="!resized"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
|
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
[class.disabled]="!resized"
|
|
||||||
icon="iqser:check"
|
icon="iqser:check"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
@ -28,20 +28,20 @@
|
|||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="resize()"
|
(action)="resize()"
|
||||||
*ngIf="annotationPermissions.canResizeAnnotation && annotations.length === 1"
|
*ngIf="annotationPermissions.canResizeAnnotation && annotations.length === 1"
|
||||||
|
[attr.help-mode-key]="helpModeKey('resize')"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="'annotation-actions.resize.label' | translate"
|
[tooltip]="'annotation-actions.resize.label' | translate"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
[attr.help-mode-key]="helpModeKey('resize')"
|
|
||||||
icon="red:resize"
|
icon="red:resize"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="annotationActionsService.editRedaction(annotations)"
|
(action)="annotationActionsService.editRedaction(annotations)"
|
||||||
*ngIf="canEdit"
|
*ngIf="canEdit"
|
||||||
|
[attr.help-mode-key]="helpModeKey('edit')"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
|
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
[attr.help-mode-key]="helpModeKey('edit')"
|
|
||||||
icon="iqser:edit"
|
icon="iqser:edit"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
@ -136,12 +136,12 @@
|
|||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
|
|
||||||
<iqser-circle-button
|
<iqser-circle-button
|
||||||
(action)="removeOrSuggestRemoveRedaction()"
|
(action)="removeRedaction()"
|
||||||
*ngIf="canRemoveRedaction"
|
*ngIf="canRemoveRedaction"
|
||||||
|
[attr.help-mode-key]="helpModeKey('remove')"
|
||||||
[tooltipPosition]="tooltipPosition"
|
[tooltipPosition]="tooltipPosition"
|
||||||
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
|
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
|
||||||
[type]="buttonType"
|
[type]="buttonType"
|
||||||
[attr.help-mode-key]="helpModeKey('remove')"
|
|
||||||
icon="iqser:trash"
|
icon="iqser:trash"
|
||||||
></iqser-circle-button>
|
></iqser-circle-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@ -24,6 +24,9 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
|
|||||||
styleUrls: ['./annotation-actions.component.scss'],
|
styleUrls: ['./annotation-actions.component.scss'],
|
||||||
})
|
})
|
||||||
export class AnnotationActionsComponent implements OnChanges {
|
export class AnnotationActionsComponent implements OnChanges {
|
||||||
|
#annotations: AnnotationWrapper[] = [];
|
||||||
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
protected _annotationId = '';
|
||||||
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
|
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
|
||||||
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
||||||
@Input() canPerformAnnotationActions: boolean;
|
@Input() canPerformAnnotationActions: boolean;
|
||||||
@ -31,13 +34,10 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
annotationPermissions: AnnotationPermissions;
|
annotationPermissions: AnnotationPermissions;
|
||||||
isImage = true;
|
isImage = true;
|
||||||
protected _annotationId = '';
|
|
||||||
#annotations: AnnotationWrapper[] = [];
|
|
||||||
readonly isVisible = computed(() => {
|
readonly isVisible = computed(() => {
|
||||||
const hidden = this._annotationManager.hidden();
|
const hidden = this._annotationManager.hidden();
|
||||||
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
|
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
|
||||||
});
|
});
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
@ -55,6 +55,13 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
return this.#annotations;
|
return this.#annotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
set annotations(annotations: AnnotationWrapper[]) {
|
||||||
|
this.#annotations = annotations.filter(a => a !== undefined);
|
||||||
|
this.isImage = this.#annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
|
||||||
|
this._annotationId = this.#annotations[0]?.id;
|
||||||
|
}
|
||||||
|
|
||||||
get canEdit(): boolean {
|
get canEdit(): boolean {
|
||||||
const canEditRedactions =
|
const canEditRedactions =
|
||||||
this.annotationPermissions.canChangeLegalBasis ||
|
this.annotationPermissions.canChangeLegalBasis ||
|
||||||
@ -91,18 +98,11 @@ export class AnnotationActionsComponent implements OnChanges {
|
|||||||
return this.annotations.every(a => a.superType === type);
|
return this.annotations.every(a => a.superType === type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input()
|
|
||||||
set annotations(annotations: AnnotationWrapper[]) {
|
|
||||||
this.#annotations = annotations.filter(a => a !== undefined);
|
|
||||||
this.isImage = this.#annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
|
|
||||||
this._annotationId = this.#annotations[0]?.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges(): void {
|
ngOnChanges(): void {
|
||||||
this.#setPermissions();
|
this.#setPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOrSuggestRemoveRedaction() {
|
removeRedaction() {
|
||||||
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
|
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +1,26 @@
|
|||||||
<div class="details">
|
<div class="details">
|
||||||
<redaction-annotation-icon
|
<redaction-annotation-icon
|
||||||
[color]="annotation.color"
|
[color]="annotation.color"
|
||||||
[label]="annotation.isEarmark ? '' : ((annotationTypesTranslations[annotation.superType] | translate)[0] | uppercase)"
|
[label]="annotation.isEarmark ? '' : ((annotationTypesTranslations[annotation.superType] | translate)?.[0] | uppercase)"
|
||||||
[type]="annotation.iconShape"
|
[type]="annotation.iconShape"
|
||||||
class="mt-6 mr-10"
|
class="mt-6 mr-10"
|
||||||
></redaction-annotation-icon>
|
></redaction-annotation-icon>
|
||||||
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
<strong>{{ annotation.superTypeLabel | translate }}</strong>
|
||||||
|
|
||||||
<strong *ngIf="annotation.pending && !annotation.isSuggestion" class="pending-analysis">
|
<strong *ngIf="annotation.pending" class="pending-analysis">
|
||||||
{{ 'annotation.pending' | translate }}
|
{{ 'annotation.pending' | translate }}
|
||||||
</strong>
|
</strong>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="annotation.entity">
|
<div *ngIf="annotation.typeLabel">
|
||||||
<strong>
|
<strong>
|
||||||
<span>{{ annotation.descriptor | translate }}</span
|
<span>{{ annotation.descriptor | translate }}</span
|
||||||
>:
|
>:
|
||||||
</strong>
|
</strong>
|
||||||
{{ annotation.recategorizationType ?? annotation.entity.label }}
|
{{ annotation.typeLabel }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { Component, inject, Input, OnChanges } from '@angular/core';
|
import { Component, inject, Input, OnChanges } from '@angular/core';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { KeysOf } from '@iqser/common-ui/lib/utils';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
import { ListItem } from '@models/file/list-item';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
|
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { ListItem } from '@models/file/list-item';
|
|
||||||
import { MultiSelectService } from '../../services/multi-select.service';
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
import { KeysOf } from '@iqser/common-ui/lib/utils';
|
|
||||||
|
|
||||||
interface Engine {
|
interface Engine {
|
||||||
readonly icon: string;
|
readonly icon: string;
|
||||||
@ -73,7 +73,7 @@ export class AnnotationDetailsComponent implements OnChanges {
|
|||||||
icon: 'red:dictionary',
|
icon: 'red:dictionary',
|
||||||
description: _('annotation-engines.dictionary'),
|
description: _('annotation-engines.dictionary'),
|
||||||
show: isBasedOn(annotation, Engines.DICTIONARY),
|
show: isBasedOn(annotation, Engines.DICTIONARY),
|
||||||
translateParams: { isHint: annotation.hint },
|
translateParams: { isHint: annotation.HINT },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'red:ai',
|
icon: 'red:ai',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<div class="active-bar-marker"></div>
|
<div class="active-bar-marker"></div>
|
||||||
|
|
||||||
<div [class.removed]="annotation.item.isChangeLogRemoved" class="annotation">
|
<div [class.removed]="annotation.item.isIgnored" class="annotation">
|
||||||
<redaction-annotation-card
|
<redaction-annotation-card
|
||||||
[annotation]="annotation.item"
|
[annotation]="annotation.item"
|
||||||
[isSelected]="annotation.isSelected"
|
[isSelected]="annotation.isSelected"
|
||||||
@ -10,26 +10,35 @@
|
|||||||
|
|
||||||
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper">
|
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper">
|
||||||
<div
|
<div
|
||||||
(click)="comments.toggleExpandComments()"
|
(click)="showComments = !showComments"
|
||||||
[matTooltip]="'comments.comments' | translate : { count: annotation.item.comments?.length }"
|
[matTooltip]="'comments.comments' | translate: { count: annotation.item.numberOfComments }"
|
||||||
class="comments-counter"
|
class="comments-counter"
|
||||||
iqserStopPropagation
|
iqserStopPropagation
|
||||||
matTooltipPosition="above"
|
matTooltipPosition="above"
|
||||||
>
|
>
|
||||||
<mat-icon svgIcon="red:comment"></mat-icon>
|
<mat-icon svgIcon="red:comment"></mat-icon>
|
||||||
{{ annotation.item.comments.length }}
|
{{ annotation.item.numberOfComments }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngIf="_multiSelectService.inactive()" class="actions">
|
<div *ngIf="multiSelectService.inactive()" class="actions">
|
||||||
<redaction-annotation-actions
|
<redaction-annotation-actions
|
||||||
[annotations]="[annotation.item]"
|
[annotations]="[annotation.item]"
|
||||||
[attr.help-mode-key]="actionsHelpModeKey"
|
[attr.help-mode-key]="actionsHelpModeKey"
|
||||||
[canPerformAnnotationActions]="_pdfProxyService.canPerformActions()"
|
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
|
||||||
></redaction-annotation-actions>
|
></redaction-annotation-actions>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-comments #comments [annotation]="annotation.item"></redaction-comments>
|
<ng-container *ngIf="showComments">
|
||||||
|
<redaction-comments [annotation]="annotation.item"></redaction-comments>
|
||||||
|
|
||||||
|
<div
|
||||||
|
(click)="showComments = false"
|
||||||
|
class="all-caps-label pointer hide-comments"
|
||||||
|
iqserStopPropagation
|
||||||
|
translate="comments.hide-comments"
|
||||||
|
></div>
|
||||||
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<redaction-annotation-details [annotation]="annotation"></redaction-annotation-details>
|
<redaction-annotation-details [annotation]="annotation"></redaction-annotation-details>
|
||||||
|
|||||||
@ -67,3 +67,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide-comments {
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-left: 26px;
|
||||||
|
}
|
||||||
|
|||||||
@ -13,12 +13,13 @@ import { ActionsHelpModeKeys } from '../../utils/constants';
|
|||||||
})
|
})
|
||||||
export class AnnotationWrapperComponent implements OnChanges {
|
export class AnnotationWrapperComponent implements OnChanges {
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
protected readonly _pdfProxyService = inject(PdfProxyService);
|
protected readonly pdfProxyService = inject(PdfProxyService);
|
||||||
protected readonly _multiSelectService = inject(MultiSelectService);
|
protected readonly multiSelectService = inject(MultiSelectService);
|
||||||
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
|
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
|
||||||
@HostBinding('attr.annotation-id') annotationId: string;
|
@HostBinding('attr.annotation-id') annotationId: string;
|
||||||
@HostBinding('class.active') active = false;
|
@HostBinding('class.active') active = false;
|
||||||
actionsHelpModeKey?: string;
|
actionsHelpModeKey?: string;
|
||||||
|
showComments = false;
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this.annotationId = this.annotation.item.id;
|
this.annotationId = this.annotation.item.id;
|
||||||
@ -28,12 +29,12 @@ export class AnnotationWrapperComponent implements OnChanges {
|
|||||||
|
|
||||||
#getActionsHelpModeKey(): string {
|
#getActionsHelpModeKey(): string {
|
||||||
if (!this.#isDocumine) {
|
if (!this.#isDocumine) {
|
||||||
const type = this.annotation.item.typeLabel?.split('.')[1];
|
const superType = this.annotation.item.superTypeLabel?.split('.')[1];
|
||||||
const typeValue = this.annotation.item.typeValue;
|
const type = this.annotation.item.type;
|
||||||
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
|
if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) {
|
||||||
return ActionsHelpModeKeys[`${type}-${typeValue}`];
|
return ActionsHelpModeKeys[`${superType}-${type}`];
|
||||||
}
|
}
|
||||||
return ActionsHelpModeKeys[type];
|
return ActionsHelpModeKeys[superType];
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,39 +1,30 @@
|
|||||||
<ng-container *ngIf="componentContext$ | async as ctx">
|
<div *ngFor="let comment of comments(); trackBy: trackBy" class="comment">
|
||||||
<div *ngFor="let comment of annotation.comments; trackBy: trackBy" class="comment">
|
<div class="comment-details-wrapper">
|
||||||
<div class="comment-details-wrapper">
|
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label">
|
||||||
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date : 'exactDate'" class="small-label">
|
<strong> {{ comment.user | name }} </strong>
|
||||||
<strong> {{ comment.user | name }} </strong>
|
{{ comment.date | date: 'sophisticatedDate' }}
|
||||||
{{ comment.date | date : 'sophisticatedDate' }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="comment-actions">
|
|
||||||
<iqser-circle-button
|
|
||||||
(action)="deleteComment(comment)"
|
|
||||||
*ngIf="permissionsService.canDeleteComment(comment, _state.file(), _state.dossier())"
|
|
||||||
[iconSize]="10"
|
|
||||||
[size]="20"
|
|
||||||
class="pointer"
|
|
||||||
icon="iqser:trash"
|
|
||||||
></iqser-circle-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>{{ comment.text }}</div>
|
<div class="comment-actions">
|
||||||
|
<iqser-circle-button
|
||||||
|
(action)="remove(comment)"
|
||||||
|
*ngIf="permissionsService.canDeleteComment(comment, state.file(), state.dossier())"
|
||||||
|
[iconSize]="10"
|
||||||
|
[size]="20"
|
||||||
|
class="pointer"
|
||||||
|
icon="iqser:trash"
|
||||||
|
></iqser-circle-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<iqser-input-with-action
|
<div>{{ comment.text }}</div>
|
||||||
(action)="addComment($event)"
|
</div>
|
||||||
*ngIf="permissionsService.canAddComment(_state.file(), _state.dossier())"
|
|
||||||
[placeholder]="'comments.add-comment' | translate"
|
|
||||||
autocomplete="off"
|
|
||||||
icon="iqser:collapse"
|
|
||||||
width="full"
|
|
||||||
></iqser-input-with-action>
|
|
||||||
|
|
||||||
<div
|
<iqser-input-with-action
|
||||||
(click)="toggleExpandComments()"
|
(action)="add($event)"
|
||||||
class="all-caps-label pointer hide-comments"
|
*ngIf="permissionsService.canAddComment(state.file(), state.dossier())"
|
||||||
iqserStopPropagation
|
[placeholder]="'comments.add-comment' | translate"
|
||||||
translate="comments.hide-comments"
|
autocomplete="off"
|
||||||
></div>
|
icon="iqser:collapse"
|
||||||
</ng-container>
|
width="full"
|
||||||
|
></iqser-input-with-action>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 8px 0 8px 16px;
|
padding: 8px 0 0 16px;
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -36,12 +36,7 @@
|
|||||||
margin: 5px 0 10px 0;
|
margin: 5px 0 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hide-comments {
|
.comment {
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment,
|
|
||||||
.hide-comments {
|
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,84 +1,73 @@
|
|||||||
import { ChangeDetectorRef, Component, HostBinding, Input, OnInit, ViewChild } from '@angular/core';
|
import { Component, inject, Input, OnChanges, signal, SimpleChanges, ViewChild } from '@angular/core';
|
||||||
import type { IComment, User } from '@red/domain';
|
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { InputWithActionComponent, LoadingService } from '@iqser/common-ui';
|
import { InputWithActionComponent, LoadingService } from '@iqser/common-ui';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { CommentingService } from '../../services/commenting.service';
|
|
||||||
import { tap } from 'rxjs/operators';
|
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
|
||||||
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
|
||||||
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
import { getCurrentUser } from '@iqser/common-ui/lib/users';
|
||||||
import { ContextComponent, trackByFactory } from '@iqser/common-ui/lib/utils';
|
import { trackByFactory } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
interface CommentsContext {
|
import type { IComment, User } from '@red/domain';
|
||||||
hiddenComments: boolean;
|
import { CommentsApiService } from '@services/comments-api.service';
|
||||||
}
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-comments',
|
selector: 'redaction-comments',
|
||||||
templateUrl: './comments.component.html',
|
templateUrl: './comments.component.html',
|
||||||
styleUrls: ['./comments.component.scss'],
|
styleUrls: ['./comments.component.scss'],
|
||||||
})
|
})
|
||||||
export class CommentsComponent extends ContextComponent<CommentsContext> implements OnInit {
|
export class CommentsComponent implements OnChanges {
|
||||||
@HostBinding('class.hidden') private _hidden = true;
|
readonly #commentsApiService = inject(CommentsApiService);
|
||||||
@ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent;
|
readonly #logger = inject(NGXLogger);
|
||||||
@Input() annotation: AnnotationWrapper;
|
@ViewChild(InputWithActionComponent) protected readonly input: InputWithActionComponent;
|
||||||
readonly trackBy = trackByFactory();
|
protected readonly trackBy = trackByFactory();
|
||||||
readonly currentUser = getCurrentUser<User>();
|
protected readonly comments = signal<IComment[]>([]);
|
||||||
hiddenComments$: Observable<boolean>;
|
protected readonly currentUser = getCurrentUser<User>();
|
||||||
|
@Input({ required: true }) annotation: AnnotationWrapper;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly permissionsService: PermissionsService,
|
readonly permissionsService: PermissionsService,
|
||||||
private readonly _manualRedactionService: ManualRedactionService,
|
|
||||||
private readonly _commentingService: CommentingService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _changeRef: ChangeDetectorRef,
|
protected readonly state: FilePreviewStateService,
|
||||||
protected readonly _state: FilePreviewStateService,
|
) {}
|
||||||
) {
|
|
||||||
super();
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
|
const currentAnnotation: AnnotationWrapper = changes.annotation?.currentValue;
|
||||||
|
const previousAnnotation: AnnotationWrapper = changes.annotation?.previousValue;
|
||||||
|
const annotationChanged = currentAnnotation?.id !== previousAnnotation?.id;
|
||||||
|
const commentsChanged = currentAnnotation?.numberOfComments !== previousAnnotation?.numberOfComments;
|
||||||
|
if (annotationChanged || commentsChanged) {
|
||||||
|
this.#logger.info(`[COMMENTS] State of annotation ${this.annotation.value} changed. Fetch comments.`);
|
||||||
|
const request = this.#commentsApiService.fetch(this.state.dossierId, this.state.fileId, this.annotation.id);
|
||||||
|
request.then(comments => this.comments.set(comments));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
async add(value: string): Promise<void> {
|
||||||
this.hiddenComments$ = this._commentingService.isActive$(this.annotation.id).pipe(
|
|
||||||
tap(active => {
|
|
||||||
this._hidden = !active;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
super._initContext({
|
|
||||||
hiddenComments: this.hiddenComments$,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async addComment(value: string): Promise<void> {
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this.state;
|
||||||
const commentId = await this._manualRedactionService.addComment(value, this.annotation.id, dossierId, fileId);
|
const commentId = await this.#commentsApiService.add(value, this.annotation.id, dossierId, fileId);
|
||||||
this.annotation.comments.push({
|
this.annotation.numberOfComments++;
|
||||||
text: value,
|
this.comments.update(current => [
|
||||||
id: commentId,
|
...current,
|
||||||
annotationId: this.annotation.id,
|
{
|
||||||
user: this.currentUser.id,
|
text: value,
|
||||||
});
|
id: commentId,
|
||||||
this._input.reset();
|
annotationId: this.annotation.id,
|
||||||
this._changeRef.markForCheck();
|
user: this.currentUser.id,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
this.input.reset();
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleExpandComments(): void {
|
async remove(comment: IComment): Promise<void> {
|
||||||
this._commentingService.toggle(this.annotation.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteComment(comment: IComment): Promise<void> {
|
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this.state;
|
||||||
await this._manualRedactionService.deleteComment(comment.id, this.annotation.id, dossierId, fileId);
|
await this.#commentsApiService.remove(comment.id, this.annotation.id, dossierId, fileId);
|
||||||
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
|
this.annotation.numberOfComments--;
|
||||||
this._changeRef.markForCheck();
|
this.comments.update(current => current.filter(c => c.id !== comment.id));
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -356,7 +356,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.viewModeService.isRedacted()) {
|
if (this.viewModeService.isRedacted()) {
|
||||||
annotations = annotations.filter(a => !bool(a.isChangeLogRemoved));
|
annotations = annotations.filter(a => !bool(a.isIgnored));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#isDocumine && !this.#isIqserDevMode) {
|
if (this.#isDocumine && !this.#isIqserDevMode) {
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
import { Component, computed, Inject } from '@angular/core';
|
import { Component, computed, Inject } from '@angular/core';
|
||||||
import { ViewMode, ViewModes } from '@red/domain';
|
|
||||||
import { ViewModeService } from '../../services/view-mode.service';
|
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
|
||||||
import { FileDataService } from '../../services/file-data.service';
|
|
||||||
import { ConfirmOptions, IConfirmationDialogData, Toaster } from '@iqser/common-ui';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
|
||||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { BASE_HREF } from '@iqser/common-ui/lib/utils';
|
import { BASE_HREF } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { ViewMode, ViewModes } from '@red/domain';
|
||||||
|
import { Roles } from '@users/roles';
|
||||||
|
import { FileDataService } from '../../services/file-data.service';
|
||||||
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
|
import { ViewModeService } from '../../services/view-mode.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-view-switch',
|
selector: 'redaction-view-switch',
|
||||||
@ -37,49 +34,12 @@ export class ViewSwitchComponent {
|
|||||||
readonly viewModeService: ViewModeService,
|
readonly viewModeService: ViewModeService,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
private readonly _dialogService: FilePreviewDialogService,
|
|
||||||
private readonly _toaster: Toaster,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
switchView(viewMode: ViewMode) {
|
switchView(viewMode: ViewMode) {
|
||||||
if (viewMode === ViewModes.REDACTED) {
|
if (viewMode === ViewModes.REDACTED) {
|
||||||
return this.#switchToRedactedView();
|
return this.viewModeService.switchToRedacted();
|
||||||
}
|
}
|
||||||
this.viewModeService.switchTo(viewMode);
|
this.viewModeService.switchTo(viewMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #switchToRedactedView() {
|
|
||||||
const suggestions = this._fileDataService.annotations().filter(a => a.isSuggestion);
|
|
||||||
if (!suggestions.length) {
|
|
||||||
return this.viewModeService.switchToRedacted();
|
|
||||||
}
|
|
||||||
|
|
||||||
const question = _('unapproved-suggestions.confirmation-dialog.not-displayed-question');
|
|
||||||
const data: IConfirmationDialogData = {
|
|
||||||
title: _('unapproved-suggestions.confirmation-dialog.title'),
|
|
||||||
question: question,
|
|
||||||
confirmationText: _('unapproved-suggestions.confirmation-dialog.confirmation-text'),
|
|
||||||
denyText: _('unapproved-suggestions.confirmation-dialog.deny-text'),
|
|
||||||
checkboxes: [
|
|
||||||
{
|
|
||||||
label: _('unapproved-suggestions.confirmation-dialog.checkbox-text'),
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
checkboxesValidation: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this._dialogService.openDialog('confirm', data, result => {
|
|
||||||
if (!result) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result === ConfirmOptions.SECOND_CONFIRM) {
|
|
||||||
this._toaster.success(_('unapproved-suggestions.confirmation-dialog.success-confirmation-text'), {
|
|
||||||
params: { settingsUrl: `${this._baseHref}/main/account/warnings-preferences` },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.viewModeService.switchToRedacted();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
<section class="dialog">
|
|
||||||
<form (submit)="save()" [formGroup]="form">
|
|
||||||
<div [translate]="dialogHeader" class="dialog-header heading-l"></div>
|
|
||||||
|
|
||||||
<div class="dialog-content">
|
|
||||||
<div class="iqser-input-group">
|
|
||||||
<label *ngIf="data.annotations.length === 1" translate="manual-annotation.dialog.content.text"></label>
|
|
||||||
</div>
|
|
||||||
{{
|
|
||||||
data.annotations.length === 1
|
|
||||||
? format(data.annotations[0].value)
|
|
||||||
: ('accept-recommendation-dialog.multiple-values' | translate)
|
|
||||||
}}
|
|
||||||
|
|
||||||
<div class="iqser-input-group required w-400">
|
|
||||||
<label translate="manual-annotation.dialog.content.dictionary"></label>
|
|
||||||
|
|
||||||
<mat-form-field>
|
|
||||||
<mat-select formControlName="dictionary">
|
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
|
||||||
<mat-option
|
|
||||||
*ngFor="let dictionary of possibleDictionaries"
|
|
||||||
[matTooltip]="dictionary.description"
|
|
||||||
[value]="dictionary.type"
|
|
||||||
matTooltipPosition="after"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{{ dictionary.label }}
|
|
||||||
</span>
|
|
||||||
</mat-option>
|
|
||||||
</mat-select>
|
|
||||||
</mat-form-field>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="iqser-input-group w-300">
|
|
||||||
<label translate="manual-annotation.dialog.content.comment"></label>
|
|
||||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dialog-actions">
|
|
||||||
<iqser-icon-button
|
|
||||||
[disabled]="form.invalid"
|
|
||||||
[label]="'manual-annotation.dialog.actions.save' | translate"
|
|
||||||
[submit]="true"
|
|
||||||
[type]="iconButtonTypes.primary"
|
|
||||||
></iqser-icon-button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
|
||||||
</section>
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
|
||||||
import { UntypedFormGroup, Validators } from '@angular/forms';
|
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { Dictionary, Dossier } from '@red/domain';
|
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|
||||||
import { acceptRecommendationTranslations } from '@translations/accept-recommendation-translations';
|
|
||||||
|
|
||||||
export interface AcceptRecommendationData {
|
|
||||||
readonly annotations: AnnotationWrapper[];
|
|
||||||
readonly dossierId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AcceptRecommendationReturnType {
|
|
||||||
readonly annotations: AnnotationWrapper[];
|
|
||||||
readonly comment: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
templateUrl: './accept-recommendation-dialog.component.html',
|
|
||||||
})
|
|
||||||
export class AcceptRecommendationDialogComponent extends BaseDialogComponent implements OnInit {
|
|
||||||
readonly dialogHeader: string;
|
|
||||||
|
|
||||||
possibleDictionaries: Dictionary[] = [];
|
|
||||||
|
|
||||||
private readonly _dossier: Dossier;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
permissionsService: PermissionsService,
|
|
||||||
activeDossiersService: ActiveDossiersService,
|
|
||||||
private readonly _dictionaryService: DictionaryService,
|
|
||||||
protected readonly _dialogRef: MatDialogRef<AcceptRecommendationDialogComponent, AcceptRecommendationReturnType>,
|
|
||||||
@Inject(MAT_DIALOG_DATA) readonly data: AcceptRecommendationData,
|
|
||||||
) {
|
|
||||||
super(_dialogRef);
|
|
||||||
this._dossier = activeDossiersService.find(this.data.dossierId);
|
|
||||||
const isDocumentAdmin = permissionsService.isApprover(this._dossier);
|
|
||||||
this.dialogHeader = isDocumentAdmin
|
|
||||||
? acceptRecommendationTranslations.addToDictionary
|
|
||||||
: acceptRecommendationTranslations.requestAddToDictionary;
|
|
||||||
this.form = this._getForm();
|
|
||||||
this.initialFormValue = this.form.getRawValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.get('dictionary').value as string;
|
|
||||||
if (dictType && this.possibleDictionaries.length) {
|
|
||||||
return this.possibleDictionaries.find(d => d.type === dictType)?.label;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.possibleDictionaries = await this._dictionaryService.getDictionariesOptions(this._dossier.dossierTemplateId, this._dossier.id);
|
|
||||||
this.form.patchValue({
|
|
||||||
dictionary: this.possibleDictionaries.find(dict => dict.type === this.data.annotations[0].recommendationType)?.type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
save() {
|
|
||||||
const recommendationType = this.form.get('dictionary').value;
|
|
||||||
this.data.annotations.forEach(a => (a.recommendationType = recommendationType));
|
|
||||||
this._dialogRef.close({
|
|
||||||
annotations: this.data.annotations,
|
|
||||||
comment: this.form.get('comment').value as string,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
format(value: string) {
|
|
||||||
return value.replace(
|
|
||||||
// eslint-disable-next-line no-control-regex,max-len
|
|
||||||
/([^\s\d-]{2,})[-\u00AD]\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]/gi,
|
|
||||||
'$1',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getForm(): UntypedFormGroup {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
dictionary: [Validators.required],
|
|
||||||
comment: [null],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { Component, Inject, OnInit } from '@angular/core';
|
import { Component, Inject, OnInit } from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|
||||||
import { UntypedFormGroup, Validators } from '@angular/forms';
|
import { UntypedFormGroup, Validators } from '@angular/forms';
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { Dossier } from '@red/domain';
|
|
||||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||||
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
import { Dossier } from '@red/domain';
|
||||||
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
export interface LegalBasisOption {
|
export interface LegalBasisOption {
|
||||||
@ -29,7 +29,7 @@ export class ChangeLegalBasisDialogComponent extends BaseDialogComponent impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
get allRectangles(): boolean {
|
get allRectangles(): boolean {
|
||||||
return this._data.annotations.reduce((acc, a) => acc && a.rectangle, true);
|
return this._data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
import { IconButtonTypes, IqserDialogComponent, IqserPermissionsService } from '@iqser/common-ui';
|
import { IconButtonTypes, IqserDialogComponent, IqserPermissionsService } from '@iqser/common-ui';
|
||||||
import { Dictionary, Dossier, SuperTypes } from '@red/domain';
|
import { Dictionary, Dossier, SuperTypes } from '@red/domain';
|
||||||
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
|
||||||
import { Roles } from '@users/roles';
|
|
||||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
|
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||||
|
import { Roles } from '@users/roles';
|
||||||
import { EditRedactionData, EditRedactResult } from '../../../utils/dialog-types';
|
import { EditRedactionData, EditRedactResult } from '../../../utils/dialog-types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -16,12 +16,12 @@ export class EditAnnotationDialogComponent
|
|||||||
extends IqserDialogComponent<EditAnnotationDialogComponent, EditRedactionData, EditRedactResult>
|
extends IqserDialogComponent<EditAnnotationDialogComponent, EditRedactionData, EditRedactResult>
|
||||||
implements OnInit
|
implements OnInit
|
||||||
{
|
{
|
||||||
|
readonly #dossier: Dossier;
|
||||||
readonly roles = Roles;
|
readonly roles = Roles;
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly redactedTexts: string[];
|
readonly redactedTexts: string[];
|
||||||
dictionaries: Dictionary[] = [];
|
dictionaries: Dictionary[] = [];
|
||||||
form: UntypedFormGroup;
|
form: UntypedFormGroup;
|
||||||
readonly #dossier: Dossier;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _justificationsService: JustificationsService,
|
private readonly _justificationsService: JustificationsService,
|
||||||
@ -79,6 +79,6 @@ export class EditAnnotationDialogComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
#allRectangles() {
|
#allRectangles() {
|
||||||
return this.data.annotations.reduce((acc, a) => acc && a.rectangle, true);
|
return this.data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<div class="dialog-content redaction">
|
<div class="dialog-content redaction">
|
||||||
<div class="iqser-input-group w-450">
|
<div class="iqser-input-group w-450">
|
||||||
<label [translate]="'resize-annotation.dialog.content.original-text'" class="selected-text"></label>
|
<label [translate]="'resize-annotation.dialog.content.original-text'" class="selected-text"></label>
|
||||||
{{ redaction.value }}
|
{{ data.redaction.value }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iqser-input-group w-450">
|
<div class="iqser-input-group w-450">
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
import { IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
import { IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|
||||||
import { Dictionary, Dossier } from '@red/domain';
|
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { ResizeAnnotationData, ResizeAnnotationResult } from '../../../utils/dialog-types';
|
import { ResizeAnnotationData, ResizeAnnotationResult } from '../../../utils/dialog-types';
|
||||||
@ -10,51 +8,26 @@ import { ResizeAnnotationData, ResizeAnnotationResult } from '../../../utils/dia
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './resize-annotation-dialog.component.html',
|
templateUrl: './resize-annotation-dialog.component.html',
|
||||||
})
|
})
|
||||||
export class ResizeAnnotationDialogComponent
|
export class ResizeAnnotationDialogComponent extends IqserDialogComponent<
|
||||||
extends IqserDialogComponent<ResizeAnnotationDialogComponent, ResizeAnnotationData, ResizeAnnotationResult>
|
ResizeAnnotationDialogComponent,
|
||||||
implements OnInit
|
ResizeAnnotationData,
|
||||||
{
|
ResizeAnnotationResult
|
||||||
readonly #dossier: Dossier;
|
> {
|
||||||
|
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
dictionaries: Dictionary[] = [];
|
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
|
||||||
redaction: AnnotationWrapper;
|
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
|
||||||
readonly form = this.#getForm();
|
readonly form = inject(FormBuilder).group({
|
||||||
|
comment: [null],
|
||||||
constructor(
|
});
|
||||||
activeDossiersService: ActiveDossiersService,
|
|
||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
|
||||||
private readonly _formBuilder: FormBuilder,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
this.#dossier = activeDossiersService.find(this.data.dossierId);
|
|
||||||
this.redaction = this.data.redaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
get displayedDictionaryLabel() {
|
|
||||||
const dictType = this.form.get('dictionary').value;
|
|
||||||
if (dictType) {
|
|
||||||
return this.dictionaries.find(d => d.type === dictType)?.label ?? null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.dictionaries = this._dictionariesMapService.get(this.#dossier.dossierTemplateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
const formValue = this.form.getRawValue();
|
const formValue = this.form.getRawValue();
|
||||||
|
|
||||||
this.close({
|
this.close({
|
||||||
comment: formValue.comment,
|
comment: formValue.comment,
|
||||||
updateDictionary: this.redaction.entity.hasDictionary,
|
updateDictionary: this.entity.hasDictionary,
|
||||||
addToAllDossiers: this.redaction.entity.hasDictionary,
|
addToAllDossiers: this.entity.hasDictionary,
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#getForm() {
|
|
||||||
return this._formBuilder.group({
|
|
||||||
comment: [null],
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export class EditRedactionDialogComponent
|
|||||||
readonly isImage: boolean;
|
readonly isImage: boolean;
|
||||||
readonly isManualRedaction: boolean;
|
readonly isManualRedaction: boolean;
|
||||||
readonly isHint: boolean;
|
readonly isHint: boolean;
|
||||||
readonly allRectangles = this.data.annotations.reduce((acc, a) => acc && a.rectangle, true);
|
readonly allRectangles = this.data.annotations.reduce((acc, a) => acc && a.AREA, true);
|
||||||
readonly showExtras: boolean;
|
readonly showExtras: boolean;
|
||||||
options: DetailsRadioOption<RedactOrHintOption>[] | undefined;
|
options: DetailsRadioOption<RedactOrHintOption>[] | undefined;
|
||||||
legalOptions: LegalBasisOption[] = [];
|
legalOptions: LegalBasisOption[] = [];
|
||||||
|
|||||||
@ -15,18 +15,18 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
|||||||
RemoveRedactionData,
|
RemoveRedactionData,
|
||||||
RemoveRedactionResult
|
RemoveRedactionResult
|
||||||
> {
|
> {
|
||||||
|
#applyToAllDossiers: boolean;
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly options: DetailsRadioOption<RemoveRedactionOption>[];
|
readonly options: DetailsRadioOption<RemoveRedactionOption>[];
|
||||||
readonly recommendation;
|
readonly recommendation;
|
||||||
readonly hint: boolean;
|
readonly hint: boolean;
|
||||||
readonly redactedTexts: string[];
|
readonly redactedTexts: string[];
|
||||||
form!: UntypedFormGroup;
|
form!: UntypedFormGroup;
|
||||||
#applyToAllDossiers: boolean;
|
|
||||||
|
|
||||||
constructor(private readonly _formBuilder: FormBuilder) {
|
constructor(private readonly _formBuilder: FormBuilder) {
|
||||||
super();
|
super();
|
||||||
this.recommendation = this.data.redactions[0].isRecommendation;
|
this.recommendation = this.data.redactions[0].isRecommendation;
|
||||||
this.hint = this.data.redactions[0].hint;
|
this.hint = this.data.redactions[0].HINT;
|
||||||
this.options = getRemoveRedactionOptions(this.data);
|
this.options = getRemoveRedactionOptions(this.data);
|
||||||
this.redactedTexts = this.data.redactions.map(annotation => annotation.value);
|
this.redactedTexts = this.data.redactions.map(annotation => annotation.value);
|
||||||
this.form = this.#getForm();
|
this.form = this.#getForm();
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<section class="dialog">
|
<section class="dialog">
|
||||||
<form (submit)="save()" [formGroup]="form">
|
<form (submit)="save()" [formGroup]="form">
|
||||||
<div
|
<div
|
||||||
[innerHTML]="'resize-redaction.dialog.header' | translate: { type: redaction.hint ? 'hint' : 'redaction' }"
|
[innerHTML]="'resize-redaction.dialog.header' | translate: { type: redaction.HINT ? 'hint' : 'redaction' }"
|
||||||
class="dialog-header heading-l"
|
class="dialog-header heading-l"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div class="dialog-content redaction">
|
<div class="dialog-content redaction">
|
||||||
<ng-container *ngIf="!redaction.isImage && !redaction.rectangle">
|
<ng-container *ngIf="!redaction.isImage && !redaction.AREA">
|
||||||
<div class="iqser-input-group w-450">
|
<div class="iqser-input-group w-450">
|
||||||
<label [translate]="'resize-redaction.dialog.content.original-text'" class="selected-text"></label>
|
<label [translate]="'resize-redaction.dialog.content.original-text'" class="selected-text"></label>
|
||||||
<span>{{ redaction.value }}</span>
|
<span>{{ redaction.value }}</span>
|
||||||
@ -26,8 +26,8 @@
|
|||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="dictionary">
|
<mat-select formControlName="dictionary">
|
||||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
||||||
<mat-option [value]="redaction.entity.type">
|
<mat-option [value]="entity.type">
|
||||||
<span> {{ redaction.entity.label }} </span>
|
<span> {{ redaction.typeLabel }} </span>
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { Component, inject, OnInit } from '@angular/core';
|
import { Component, inject } from '@angular/core';
|
||||||
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
|
||||||
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||||
import { Dictionary } from '@red/domain';
|
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||||
import { getResizeRedactionOptions, ResizeOptions, ResizeRedactionOption } from '../../utils/dialog-options';
|
import { getResizeRedactionOptions, ResizeOptions, ResizeRedactionOption } from '../../utils/dialog-options';
|
||||||
@ -10,15 +9,17 @@ import { ResizeRedactionData, ResizeRedactionResult } from '../../utils/dialog-t
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: './resize-redaction-dialog.component.html',
|
templateUrl: './resize-redaction-dialog.component.html',
|
||||||
})
|
})
|
||||||
export class ResizeRedactionDialogComponent
|
export class ResizeRedactionDialogComponent extends IqserDialogComponent<
|
||||||
extends IqserDialogComponent<ResizeRedactionDialogComponent, ResizeRedactionData, ResizeRedactionResult>
|
ResizeRedactionDialogComponent,
|
||||||
implements OnInit
|
ResizeRedactionData,
|
||||||
{
|
ResizeRedactionResult
|
||||||
|
> {
|
||||||
readonly #applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
readonly #applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||||
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
readonly #dossier = inject(ActiveDossiersService).find(this.data.dossierId);
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly options: DetailsRadioOption<ResizeRedactionOption>[];
|
readonly options: DetailsRadioOption<ResizeRedactionOption>[];
|
||||||
dictionaries: Dictionary[] = [];
|
readonly dictionaries = inject(DictionariesMapService).get(this.#dossier.dossierTemplateId);
|
||||||
|
readonly entity = this.dictionaries.find(d => d.type === this.data.redaction.type);
|
||||||
readonly redaction = this.data.redaction;
|
readonly redaction = this.data.redaction;
|
||||||
readonly form: FormGroup<{
|
readonly form: FormGroup<{
|
||||||
comment: FormControl<string>;
|
comment: FormControl<string>;
|
||||||
@ -26,10 +27,7 @@ export class ResizeRedactionDialogComponent
|
|||||||
option: FormControl<DetailsRadioOption<ResizeRedactionOption>>;
|
option: FormControl<DetailsRadioOption<ResizeRedactionOption>>;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
constructor(
|
constructor(private readonly _formBuilder: FormBuilder) {
|
||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
|
||||||
private readonly _formBuilder: FormBuilder,
|
|
||||||
) {
|
|
||||||
super();
|
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.form = this.#getForm();
|
this.form = this.#getForm();
|
||||||
@ -43,10 +41,6 @@ export class ResizeRedactionDialogComponent
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.dictionaries = this._dictionariesMapService.get(this.#dossier.dossierTemplateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
const formValue = this.form.getRawValue();
|
const formValue = this.form.getRawValue();
|
||||||
const updateDictionary = formValue.option.value === ResizeOptions.IN_DOSSIER;
|
const updateDictionary = formValue.option.value === ResizeOptions.IN_DOSSIER;
|
||||||
@ -61,7 +55,7 @@ export class ResizeRedactionDialogComponent
|
|||||||
#getForm() {
|
#getForm() {
|
||||||
return this._formBuilder.group({
|
return this._formBuilder.group({
|
||||||
comment: [null],
|
comment: [null],
|
||||||
dictionary: new FormControl({ value: this.redaction.entity.type, disabled: true }),
|
dictionary: new FormControl({ value: this.redaction.typeLabel, disabled: true }),
|
||||||
option: this.options[0],
|
option: this.options[0],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,21 @@
|
|||||||
import { ExcludedPagesService } from './services/excluded-pages.service';
|
|
||||||
import { ViewModeService } from './services/view-mode.service';
|
|
||||||
import { MultiSelectService } from './services/multi-select.service';
|
|
||||||
import { DocumentInfoService } from './services/document-info.service';
|
|
||||||
import { CommentingService } from './services/commenting.service';
|
|
||||||
import { SkippedService } from './services/skipped.service';
|
|
||||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
|
||||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
|
||||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
|
||||||
import { EntitiesService, ListingService, SearchService } from '@iqser/common-ui';
|
import { EntitiesService, ListingService, SearchService } from '@iqser/common-ui';
|
||||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
|
||||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
|
||||||
import { FileDataService } from './services/file-data.service';
|
|
||||||
import { AnnotationsListingService } from './services/annotations-listing.service';
|
|
||||||
import { StampService } from './services/stamp.service';
|
|
||||||
import { PdfProxyService } from './services/pdf-proxy.service';
|
|
||||||
import { PdfAnnotationActionsService } from './services/pdf-annotation-actions.service';
|
|
||||||
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
import { FilterService } from '@iqser/common-ui/lib/filtering';
|
||||||
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
import { SortingService } from '@iqser/common-ui/lib/sorting';
|
||||||
|
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||||
|
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||||
|
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||||
|
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||||
|
import { AnnotationsListingService } from './services/annotations-listing.service';
|
||||||
|
import { DocumentInfoService } from './services/document-info.service';
|
||||||
|
import { ExcludedPagesService } from './services/excluded-pages.service';
|
||||||
|
import { FileDataService } from './services/file-data.service';
|
||||||
|
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||||
|
import { MultiSelectService } from './services/multi-select.service';
|
||||||
|
import { PdfAnnotationActionsService } from './services/pdf-annotation-actions.service';
|
||||||
|
import { PdfProxyService } from './services/pdf-proxy.service';
|
||||||
|
import { SkippedService } from './services/skipped.service';
|
||||||
|
import { StampService } from './services/stamp.service';
|
||||||
|
import { ViewModeService } from './services/view-mode.service';
|
||||||
|
|
||||||
export const filePreviewScreenProviders = [
|
export const filePreviewScreenProviders = [
|
||||||
FilterService,
|
FilterService,
|
||||||
@ -24,7 +23,6 @@ export const filePreviewScreenProviders = [
|
|||||||
ViewModeService,
|
ViewModeService,
|
||||||
MultiSelectService,
|
MultiSelectService,
|
||||||
DocumentInfoService,
|
DocumentInfoService,
|
||||||
CommentingService,
|
|
||||||
SkippedService,
|
SkippedService,
|
||||||
AnnotationActionsService,
|
AnnotationActionsService,
|
||||||
PdfAnnotationActionsService,
|
PdfAnnotationActionsService,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { OverlayModule } from '@angular/cdk/overlay';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
|
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
|
||||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||||
import {
|
import {
|
||||||
CapitalizePipe,
|
CapitalizePipe,
|
||||||
@ -49,7 +50,6 @@ import { ReadonlyBannerComponent } from './components/readonly-banner/readonly-b
|
|||||||
import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component';
|
import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component';
|
||||||
import { UserManagementComponent } from './components/user-management/user-management.component';
|
import { UserManagementComponent } from './components/user-management/user-management.component';
|
||||||
import { ViewSwitchComponent } from './components/view-switch/view-switch.component';
|
import { ViewSwitchComponent } from './components/view-switch/view-switch.component';
|
||||||
import { AcceptRecommendationDialogComponent } from './dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component';
|
|
||||||
import { AddHintDialogComponent } from './dialogs/add-hint-dialog/add-hint-dialog.component';
|
import { AddHintDialogComponent } from './dialogs/add-hint-dialog/add-hint-dialog.component';
|
||||||
import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
|
import { ChangeLegalBasisDialogComponent } from './dialogs/change-legal-basis-dialog/change-legal-basis-dialog.component';
|
||||||
import { AddAnnotationDialogComponent } from './dialogs/docu-mine/add-annotation-dialog/add-annotation-dialog.component';
|
import { AddAnnotationDialogComponent } from './dialogs/docu-mine/add-annotation-dialog/add-annotation-dialog.component';
|
||||||
@ -71,7 +71,6 @@ import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
|||||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||||
import { TablesService } from './services/tables.service';
|
import { TablesService } from './services/tables.service';
|
||||||
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
|
|
||||||
|
|
||||||
const routes: IqserRoutes = [
|
const routes: IqserRoutes = [
|
||||||
{
|
{
|
||||||
@ -88,7 +87,6 @@ const dialogs = [
|
|||||||
ResizeRedactionDialogComponent,
|
ResizeRedactionDialogComponent,
|
||||||
ChangeLegalBasisDialogComponent,
|
ChangeLegalBasisDialogComponent,
|
||||||
HighlightActionDialogComponent,
|
HighlightActionDialogComponent,
|
||||||
AcceptRecommendationDialogComponent,
|
|
||||||
DocumentInfoDialogComponent,
|
DocumentInfoDialogComponent,
|
||||||
ImportRedactionsDialogComponent,
|
ImportRedactionsDialogComponent,
|
||||||
RedactTextDialogComponent,
|
RedactTextDialogComponent,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
|
import { IqserDialog } from '@common-ui/dialog/iqser-dialog.service';
|
||||||
import { getConfig, Toaster } from '@iqser/common-ui';
|
import { getConfig, Toaster } from '@iqser/common-ui';
|
||||||
import { List, log } from '@iqser/common-ui/lib/utils';
|
import { List, log } from '@iqser/common-ui/lib/utils';
|
||||||
@ -13,6 +13,7 @@ import {
|
|||||||
IRectangle,
|
IRectangle,
|
||||||
IResizeRequest,
|
IResizeRequest,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
|
import { CommentsApiService } from '@services/comments-api.service';
|
||||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { firstValueFrom, Observable, zip } from 'rxjs';
|
import { firstValueFrom, Observable, zip } from 'rxjs';
|
||||||
@ -46,6 +47,7 @@ import { SkippedService } from './skipped.service';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnnotationActionsService {
|
export class AnnotationActionsService {
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
readonly #commentsApiService = inject(CommentsApiService);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _manualRedactionService: ManualRedactionService,
|
private readonly _manualRedactionService: ManualRedactionService,
|
||||||
@ -129,7 +131,7 @@ export class AnnotationActionsService {
|
|||||||
if (result.comment) {
|
if (result.comment) {
|
||||||
try {
|
try {
|
||||||
for (const a of annotations) {
|
for (const a of annotations) {
|
||||||
await this._manualRedactionService.addComment(result.comment, a.id, dossierId, fileId);
|
await this.#commentsApiService.add(result.comment, a.id, dossierId, fileId);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this._toaster.rawError(error.error.message);
|
this._toaster.rawError(error.error.message);
|
||||||
@ -208,7 +210,7 @@ export class AnnotationActionsService {
|
|||||||
resize(annotationWrapper: AnnotationWrapper) {
|
resize(annotationWrapper: AnnotationWrapper) {
|
||||||
this._annotationManager.resizingAnnotationId = annotationWrapper.id;
|
this._annotationManager.resizingAnnotationId = annotationWrapper.id;
|
||||||
|
|
||||||
if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) {
|
if (annotationWrapper.AREA || annotationWrapper.imported || annotationWrapper.isImage) {
|
||||||
this._annotationManager.delete(annotationWrapper);
|
this._annotationManager.delete(annotationWrapper);
|
||||||
const rectangleAnnotation = this.#generateRectangle(annotationWrapper);
|
const rectangleAnnotation = this.#generateRectangle(annotationWrapper);
|
||||||
return this._annotationManager.add(rectangleAnnotation);
|
return this._annotationManager.add(rectangleAnnotation);
|
||||||
@ -236,7 +238,7 @@ export class AnnotationActionsService {
|
|||||||
const dossier = this._state.dossier();
|
const dossier = this._state.dossier();
|
||||||
|
|
||||||
const isImageText = annotation.isImage ? 'Image' : textAndPositions.text;
|
const isImageText = annotation.isImage ? 'Image' : textAndPositions.text;
|
||||||
const text = annotation.rectangle ? annotation.value : isImageText;
|
const text = annotation.AREA ? annotation.value : isImageText;
|
||||||
const isApprover = this._permissionsService.isApprover(dossier);
|
const isApprover = this._permissionsService.isApprover(dossier);
|
||||||
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
const dossierTemplate = this._dossierTemplatesService.find(this._state.dossierTemplateId);
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export class AnnotationProcessingService {
|
|||||||
label: _('filter-menu.with-comments'),
|
label: _('filter-menu.with-comments'),
|
||||||
checked: false,
|
checked: false,
|
||||||
topLevelFilter: true,
|
topLevelFilter: true,
|
||||||
checker: (annotation: AnnotationWrapper) => annotation?.comments?.length > 0,
|
checker: (annotation: AnnotationWrapper) => annotation?.numberOfComments > 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'redaction-changes',
|
id: 'redaction-changes',
|
||||||
@ -106,7 +106,7 @@ export class AnnotationProcessingService {
|
|||||||
}
|
}
|
||||||
const childFilter: IFilter = {
|
const childFilter: IFilter = {
|
||||||
id: a.filterKey,
|
id: a.filterKey,
|
||||||
label: a.entity.label,
|
label: a.typeLabel,
|
||||||
checked: false,
|
checked: false,
|
||||||
matches: 1,
|
matches: 1,
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
|
||||||
import { map, startWith } from 'rxjs/operators';
|
|
||||||
import { shareDistinctLast } from '@iqser/common-ui/lib/utils';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CommentingService {
|
|
||||||
private _activeAnnotations = new BehaviorSubject<Set<string>>(new Set<string>());
|
|
||||||
|
|
||||||
/** Annotations with active comments section */
|
|
||||||
isActive$(annotationId: string): Observable<boolean> {
|
|
||||||
return this._activeAnnotations.pipe(
|
|
||||||
map(annotations => annotations.has(annotationId)),
|
|
||||||
startWith(false),
|
|
||||||
shareDistinctLast(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle(annotationId: string): void {
|
|
||||||
if (this._activeAnnotations.value.has(annotationId)) {
|
|
||||||
this._deactivate(annotationId);
|
|
||||||
} else {
|
|
||||||
this._activate(annotationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _activate(annotationId: string): void {
|
|
||||||
const currentValue = this._activeAnnotations.value;
|
|
||||||
const newSet = new Set<string>(currentValue).add(annotationId);
|
|
||||||
this._activeAnnotations.next(newSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _deactivate(annotationId: string): void {
|
|
||||||
const currentValue = this._activeAnnotations.value;
|
|
||||||
const newSet = new Set<string>(currentValue);
|
|
||||||
newSet.delete(annotationId);
|
|
||||||
this._activeAnnotations.next(newSet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { effect, Injectable, Signal, signal } from '@angular/core';
|
import { effect, inject, Injectable, Signal, signal } from '@angular/core';
|
||||||
import { toObservable } from '@angular/core/rxjs-interop';
|
import { toObservable } from '@angular/core/rxjs-interop';
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
||||||
@ -6,15 +6,15 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|||||||
import {
|
import {
|
||||||
ChangeType,
|
ChangeType,
|
||||||
ChangeTypes,
|
ChangeTypes,
|
||||||
|
EntryStates,
|
||||||
File,
|
File,
|
||||||
IRedactionLog,
|
IEntityLog,
|
||||||
IRedactionLogEntry,
|
IEntityLogEntry,
|
||||||
LogEntryStatuses,
|
SuperTypeMapper,
|
||||||
ViewedPage,
|
ViewedPage,
|
||||||
ViewMode,
|
ViewMode,
|
||||||
ViewModes,
|
ViewModes,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { EarmarksService } from '@services/files/earmarks.service';
|
import { EarmarksService } from '@services/files/earmarks.service';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
@ -46,6 +46,8 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
|
readonly #earmarks = signal<Map<number, AnnotationWrapper[]>>(new Map());
|
||||||
#originalViewedPages: ViewedPage[] = [];
|
#originalViewedPages: ViewedPage[] = [];
|
||||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||||
|
readonly #logger = inject(NGXLogger);
|
||||||
|
readonly #toaster = inject(Toaster);
|
||||||
protected readonly _entityClass = AnnotationWrapper;
|
protected readonly _entityClass = AnnotationWrapper;
|
||||||
missingTypes = new Set<string>();
|
missingTypes = new Set<string>();
|
||||||
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
|
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
|
||||||
@ -64,9 +66,6 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
private readonly _earmarksService: EarmarksService,
|
private readonly _earmarksService: EarmarksService,
|
||||||
private readonly _multiSelectService: MultiSelectService,
|
private readonly _multiSelectService: MultiSelectService,
|
||||||
private readonly _filesService: FilesService,
|
private readonly _filesService: FilesService,
|
||||||
private readonly _toaster: Toaster,
|
|
||||||
private readonly _logger: NGXLogger,
|
|
||||||
private readonly _defaultColorsService: DefaultColorsService,
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.annotations$ = toObservable(this.#annotations);
|
this.annotations$ = toObservable(this.#annotations);
|
||||||
@ -101,16 +100,16 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
async loadAnnotations(file: File) {
|
async loadAnnotations(file: File) {
|
||||||
if (!file || file.isUnprocessed) {
|
if (!file || file.isUnprocessed) {
|
||||||
this._logger.info('[ANNOTATIONS] File is null or unprocessed, skipping annotations loading');
|
this.#logger.info('[ANNOTATIONS] File is null or unprocessed, skipping annotations loading');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logger.info('[ANNOTATIONS] Loading annotations...');
|
this.#logger.info('[ANNOTATIONS] Loading annotations...');
|
||||||
|
|
||||||
await this.#loadViewedPages(file);
|
await this.#loadViewedPages(file);
|
||||||
await this.loadRedactionLog();
|
await this.loadRedactionLog();
|
||||||
|
|
||||||
this._logger.info('[ANNOTATIONS] Annotations loaded');
|
this.#logger.info('[ANNOTATIONS] Annotations loaded');
|
||||||
}
|
}
|
||||||
|
|
||||||
async annotationsChanged() {
|
async annotationsChanged() {
|
||||||
@ -131,10 +130,10 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadRedactionLog() {
|
async loadRedactionLog() {
|
||||||
this._logger.info('[REDACTION_LOG] Loading redaction log...');
|
this.#logger.info('[REDACTION_LOG] Loading redaction log...');
|
||||||
const redactionLog = await this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId);
|
const redactionLog = await this._redactionLogService.getEntityLog(this._state.dossierId, this._state.fileId);
|
||||||
|
|
||||||
this._logger.info('[REDACTION_LOG] Redaction log loaded', redactionLog);
|
this.#logger.info('[REDACTION_LOG] Redaction log loaded', redactionLog);
|
||||||
let annotations = await this.#convertData(redactionLog);
|
let annotations = await this.#convertData(redactionLog);
|
||||||
this.#checkMissingTypes();
|
this.#checkMissingTypes();
|
||||||
annotations = this._userPreferenceService.isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive);
|
annotations = this._userPreferenceService.isIqserDevMode ? annotations : annotations.filter(a => !a.isFalsePositive);
|
||||||
@ -143,7 +142,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
#checkMissingTypes() {
|
#checkMissingTypes() {
|
||||||
if (this.missingTypes.size > 0) {
|
if (this.missingTypes.size > 0) {
|
||||||
this._toaster.error(_('error.missing-types'), {
|
this.#toaster.error(_('error.missing-types'), {
|
||||||
disableTimeOut: true,
|
disableTimeOut: true,
|
||||||
params: { missingTypes: Array.from(this.missingTypes).join(', ') },
|
params: { missingTypes: Array.from(this.missingTypes).join(', ') },
|
||||||
});
|
});
|
||||||
@ -154,50 +153,68 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
async #loadViewedPages(file: File) {
|
async #loadViewedPages(file: File) {
|
||||||
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
|
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||||
this._viewedPagesMapService.set(file.fileId, []);
|
this._viewedPagesMapService.set(file.fileId, []);
|
||||||
this._logger.info('[VIEWED_PAGES] Cannot mark pages as viewed, skip loading viewed pages');
|
this.#logger.info('[VIEWED_PAGES] Cannot mark pages as viewed, skip loading viewed pages');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#originalViewedPages = await this._viewedPagesService.load(file.dossierId, file.fileId);
|
this.#originalViewedPages = await this._viewedPagesService.load(file.dossierId, file.fileId);
|
||||||
this._logger.info('[VIEWED_PAGES] Loaded viewed pages', this.#originalViewedPages);
|
this.#logger.info('[VIEWED_PAGES] Loaded viewed pages', this.#originalViewedPages);
|
||||||
this._viewedPagesMapService.set(file.fileId, this.#originalViewedPages);
|
this._viewedPagesMapService.set(file.fileId, this.#originalViewedPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
|
#getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
|
||||||
return annotations.filter(annotation => {
|
return annotations.filter(annotation => {
|
||||||
if (viewMode === 'STANDARD') {
|
if (viewMode === 'STANDARD') {
|
||||||
return !annotation.isChangeLogRemoved;
|
return !annotation.isIgnored;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewMode === 'DELTA') {
|
if (viewMode === 'DELTA') {
|
||||||
return annotation.isChangeLogEntry;
|
return annotation.isChangeLogEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
return annotation.previewAnnotation;
|
return annotation.isRedacted;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async #convertData(redactionLog: IRedactionLog) {
|
async #convertData(entityLog: IEntityLog) {
|
||||||
if (!redactionLog.redactionLogEntry) {
|
if (!entityLog.entityLogEntry) {
|
||||||
return [];
|
return [] as AnnotationWrapper[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = this._state.file();
|
const file = this._state.file();
|
||||||
const annotations: AnnotationWrapper[] = [];
|
const annotations: AnnotationWrapper[] = [];
|
||||||
const sourceIds: Record<string, AnnotationWrapper[]> = {};
|
|
||||||
const dictionaries = this._state.dictionaries;
|
const dictionaries = this._state.dictionaries;
|
||||||
const defaultColors = this._defaultColorsService.find(this._state.dossierTemplateId);
|
|
||||||
let checkDictionary = true;
|
let checkDictionary = true;
|
||||||
|
|
||||||
for (const entry of redactionLog.redactionLogEntry) {
|
for (const entry of entityLog.entityLogEntry) {
|
||||||
const pageNumber = entry.positions[0]?.page;
|
const pageNumber = entry.positions[0].pageNumber;
|
||||||
const manual = entry.manualChanges?.length > 0;
|
const manual = entry.manualChanges.length > 0;
|
||||||
if (!manual && file.excludedPages.includes(pageNumber)) {
|
if (!manual && file.excludedPages.includes(pageNumber)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changeLogValues = this.#getChangeLogValues(entry, file);
|
if (entry.state === EntryStates.REMOVED) {
|
||||||
if (changeLogValues.hidden) {
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const canBeMappedToASuperType = !!SuperTypeMapper[entry.entryType][entry.state](entry);
|
||||||
|
if (!canBeMappedToASuperType && this._userPreferenceService.isIqserDevMode) {
|
||||||
|
this.#logger.error(
|
||||||
|
`[ENTITY_LOG] Entity ${entry.value} (${entry.entryType}, ${entry.state}) cannot be mapped to a super type!`,
|
||||||
|
entry,
|
||||||
|
);
|
||||||
|
this.#toaster.rawError(
|
||||||
|
`Skipping entity ${entry.value} (${entry.entryType}, ${entry.state}). It has unexpected state.
|
||||||
|
Check console for details.`,
|
||||||
|
{
|
||||||
|
timeOut: 15000,
|
||||||
|
easing: 'ease-in-out',
|
||||||
|
easeTime: 500,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canBeMappedToASuperType) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,56 +231,29 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotation = AnnotationWrapper.fromData(
|
const changeType = this.#getChangeLogType(entry, file);
|
||||||
entry,
|
const annotation = AnnotationWrapper.fromData(entry, dictionary, changeType, entityLog.legalBasis ?? [], this.#isDocumine);
|
||||||
dictionaries,
|
|
||||||
defaultColors,
|
|
||||||
changeLogValues.changeLogType,
|
|
||||||
redactionLog.legalBasis ?? [],
|
|
||||||
!!dictionary?.hint,
|
|
||||||
this.#isDocumine,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (entry.sourceId) {
|
|
||||||
if (!sourceIds[entry.sourceId]) {
|
|
||||||
sourceIds[entry.sourceId] = [];
|
|
||||||
}
|
|
||||||
sourceIds[entry.sourceId].push(annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
annotations.push(annotation);
|
annotations.push(annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceKeys = Object.keys(sourceIds);
|
return annotations;
|
||||||
return annotations.filter(r => !sourceKeys.includes(r.id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#getChangeLogValues(
|
#getChangeLogType(entityLogEntry: IEntityLogEntry, file: File): ChangeType | undefined {
|
||||||
redactionLogEntry: IRedactionLogEntry,
|
const hasManualChanges = entityLogEntry.manualChanges?.length > 0;
|
||||||
file: File,
|
|
||||||
): {
|
|
||||||
hidden: boolean;
|
|
||||||
changeLogType?: ChangeType;
|
|
||||||
} {
|
|
||||||
const hasManualChanges = redactionLogEntry.manualChanges?.length > 0;
|
|
||||||
if (file.numberOfAnalyses <= 1 && !file.hasUpdates && !hasManualChanges) {
|
if (file.numberOfAnalyses <= 1 && !file.hasUpdates && !hasManualChanges) {
|
||||||
return {
|
return;
|
||||||
changeLogType: undefined,
|
|
||||||
hidden: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const viableChanges = redactionLogEntry.changes?.filter(c => c.analysisNumber > 1);
|
const viableChanges = entityLogEntry.changes?.filter(c => c.analysisNumber > 1);
|
||||||
const lastChange = viableChanges?.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
const lastChange = viableChanges?.sort(chronologicallyBy(x => x.dateTime)).at(-1);
|
||||||
|
|
||||||
const page = redactionLogEntry.positions?.[0].page;
|
const page = entityLogEntry.positions[0].pageNumber;
|
||||||
const viewedPage = this.#originalViewedPages.find(p => p.page === page);
|
const viewedPage = this.#originalViewedPages.find(p => p.page === page);
|
||||||
|
|
||||||
if (!viewedPage) {
|
if (!viewedPage) {
|
||||||
return {
|
return;
|
||||||
changeLogType: undefined,
|
|
||||||
hidden: lastChange ? lastChange.type === ChangeTypes.REMOVED : false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// page has been seen -> let's see if it's a change
|
// page has been seen -> let's see if it's a change
|
||||||
@ -272,33 +262,19 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
if (changeOccurredAfterPageIsViewed) {
|
if (changeOccurredAfterPageIsViewed) {
|
||||||
this.#markPageAsUnseenIfNeeded(viewedPage, lastChange.dateTime);
|
this.#markPageAsUnseenIfNeeded(viewedPage, lastChange.dateTime);
|
||||||
return {
|
return lastChange?.type;
|
||||||
changeLogType: lastChange?.type,
|
|
||||||
hidden: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibleStatuses: string[] = [LogEntryStatuses.APPROVED, LogEntryStatuses.REQUESTED];
|
const viableManualChanges = entityLogEntry.manualChanges.filter(change => change.processed);
|
||||||
const viableManualChanges = redactionLogEntry.manualChanges.filter(change => visibleStatuses.includes(change.annotationStatus));
|
|
||||||
viableManualChanges.sort(chronologicallyBy(x => x.processedDate));
|
viableManualChanges.sort(chronologicallyBy(x => x.processedDate));
|
||||||
const lastManualChange = viableManualChanges.at(-1);
|
const lastManualChange = viableManualChanges.at(-1);
|
||||||
const isSuggestion = lastManualChange?.annotationStatus === LogEntryStatuses.REQUESTED;
|
const processedTime = lastManualChange?.processedDate;
|
||||||
const processedTime = isSuggestion ? lastManualChange.requestedDate : lastManualChange?.processedDate;
|
|
||||||
changeOccurredAfterPageIsViewed = processedTime && timestampOf(processedTime) > viewTime;
|
changeOccurredAfterPageIsViewed = processedTime && timestampOf(processedTime) > viewTime;
|
||||||
|
|
||||||
if (changeOccurredAfterPageIsViewed) {
|
if (changeOccurredAfterPageIsViewed) {
|
||||||
this.#markPageAsUnseenIfNeeded(viewedPage, processedTime);
|
this.#markPageAsUnseenIfNeeded(viewedPage, processedTime);
|
||||||
return {
|
return ChangeTypes.CHANGED;
|
||||||
changeLogType: ChangeTypes.CHANGED,
|
|
||||||
hidden: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page doesn't have a view-time or no relevant changes - hide removed anyway
|
|
||||||
return {
|
|
||||||
changeLogType: undefined,
|
|
||||||
hidden: lastChange ? lastChange.type === ChangeTypes.REMOVED : false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#markPageAsUnseenIfNeeded(viewedPage: ViewedPage, processedTime: string) {
|
#markPageAsUnseenIfNeeded(viewedPage: ViewedPage, processedTime: string) {
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|||||||
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
|
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
|
||||||
import type {
|
import type {
|
||||||
DictionaryActions,
|
DictionaryActions,
|
||||||
Dossier,
|
|
||||||
IAddRedactionRequest,
|
IAddRedactionRequest,
|
||||||
ILegalBasisChangeRequest,
|
ILegalBasisChangeRequest,
|
||||||
IManualAddResponse,
|
IManualAddResponse,
|
||||||
@ -16,12 +15,10 @@ import type {
|
|||||||
IResizeRequest,
|
IResizeRequest,
|
||||||
ManualRedactionActions,
|
ManualRedactionActions,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
|
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
|
||||||
import { Roles } from '@users/roles';
|
import { Roles } from '@users/roles';
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { firstValueFrom, of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
|
|
||||||
function getResponseType(error: boolean, isConflict: boolean) {
|
function getResponseType(error: boolean, isConflict: boolean) {
|
||||||
@ -41,30 +38,16 @@ function getMessage(action: ManualRedactionActions, isDictionary = false, error
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||||
protected readonly _defaultModelPath = 'manualRedaction';
|
protected readonly _defaultModelPath = 'manualRedaction';
|
||||||
readonly #bulkRequest = `${this._defaultModelPath}/bulk/request`;
|
|
||||||
readonly #bulkRedaction = `${this._defaultModelPath}/bulk/redaction`;
|
readonly #bulkRedaction = `${this._defaultModelPath}/bulk/redaction`;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _logger: NGXLogger,
|
private readonly _logger: NGXLogger,
|
||||||
private readonly _permissionsService: PermissionsService,
|
private readonly _iqserPermissionsService: IqserPermissionsService,
|
||||||
private readonly _iqaerPermissionsService: IqserPermissionsService,
|
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async addComment(comment: string, annotationId: string, dossierId: string, fileId: string) {
|
|
||||||
const url = `${this._defaultModelPath}/comment/add/${dossierId}/${fileId}/${annotationId}`;
|
|
||||||
const request = await firstValueFrom(this._post<{ commentId: string }>({ text: comment }, url));
|
|
||||||
return request.commentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteComment(commentId: string, annotationId: string, dossierId: string, fileId: string) {
|
|
||||||
const url = `${this._defaultModelPath}/comment/undo/${dossierId}/${fileId}/${annotationId}/${commentId}`;
|
|
||||||
return firstValueFrom(super.delete({}, url));
|
|
||||||
}
|
|
||||||
|
|
||||||
addRecommendation(annotations: AnnotationWrapper[], redaction: IAddRedactionRequest, dossierId: string, fileId: string) {
|
addRecommendation(annotations: AnnotationWrapper[], redaction: IAddRedactionRequest, dossierId: string, fileId: string) {
|
||||||
const recommendations: List<IAddRedactionRequest> = annotations.map(annotation => ({
|
const recommendations: List<IAddRedactionRequest> = annotations.map(annotation => ({
|
||||||
addToDictionary: redaction.addToDictionary,
|
addToDictionary: redaction.addToDictionary,
|
||||||
@ -96,7 +79,7 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
const toast = requests[0].addToDictionary
|
const toast = requests[0].addToDictionary
|
||||||
? this.#showAddToDictionaryToast(requests, options?.dictionaryLabel)
|
? this.#showAddToDictionaryToast(requests, options?.dictionaryLabel)
|
||||||
: this.#showToast(options?.hint ? 'force-hint' : 'add');
|
: this.#showToast(options?.hint ? 'force-hint' : 'add');
|
||||||
const canAddRedaction = this._iqaerPermissionsService.has(Roles.redactions.write);
|
const canAddRedaction = this._iqserPermissionsService.has(Roles.redactions.write);
|
||||||
if (canAddRedaction) {
|
if (canAddRedaction) {
|
||||||
return this.add(requests, dossierId, fileId).pipe(toast);
|
return this.add(requests, dossierId, fileId).pipe(toast);
|
||||||
}
|
}
|
||||||
@ -112,14 +95,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
|
return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
|
||||||
}
|
}
|
||||||
|
|
||||||
declineOrRemove(annotationIds: List, dossierId: string, fileId: string, modifyDictionary = false) {
|
|
||||||
if (this._permissionsService.isApprover(this.#dossier(dossierId))) {
|
|
||||||
return this.decline(annotationIds, dossierId, fileId).pipe(this.#showToast('decline', modifyDictionary));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary));
|
|
||||||
}
|
|
||||||
|
|
||||||
removeRedaction(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) {
|
removeRedaction(body: List<IRemoveRedactionRequest>, dossierId: string, fileId: string, removeFromDictionary = false, isHint = false) {
|
||||||
return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary));
|
return this.remove(body, dossierId, fileId).pipe(this.#showToast(!isHint ? 'remove' : 'remove-hint', removeFromDictionary));
|
||||||
}
|
}
|
||||||
@ -151,25 +126,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestLegalBasisChange(body: List<ILegalBasisChangeRequest>, dossierId: string, fileId: string) {
|
|
||||||
return this._post(body, `${this.#bulkRequest}/legalBasis/${dossierId}/${fileId}`).pipe(
|
|
||||||
this.#log('Request legal basis change', body),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
approve(annotationIds: List, dossierId: string, fileId: string) {
|
|
||||||
return this._post(annotationIds, `${this._defaultModelPath}/bulk/approve/${dossierId}/${fileId}`).pipe(
|
|
||||||
this.#log('Approve', annotationIds),
|
|
||||||
this.#showToast('approve'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
decline(annotationIds: List, dossierId: string, fileId: string) {
|
|
||||||
return this._post(annotationIds, `${this._defaultModelPath}/bulk/decline/${dossierId}/${fileId}`).pipe(
|
|
||||||
this.#log('Decline', annotationIds),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(annotationIds: List, dossierId: string, fileId: string) {
|
undo(annotationIds: List, dossierId: string, fileId: string) {
|
||||||
const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`;
|
const url = `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`;
|
||||||
return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds));
|
return super.delete(annotationIds, url).pipe(this.#log('Undo', annotationIds));
|
||||||
@ -222,8 +178,4 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#dossier(dossierId: string): Dossier {
|
|
||||||
return this._activeDossiersService.find(dossierId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -344,7 +344,7 @@ export class PdfProxyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(wrapper.rectangle || wrapper.isImage || wrapper.imported) &&
|
(wrapper.AREA || wrapper.isImage || wrapper.imported) &&
|
||||||
annotation.ToolName !== AnnotationToolNames.AnnotationCreateRectangle
|
annotation.ToolName !== AnnotationToolNames.AnnotationCreateRectangle
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -159,7 +159,7 @@ export const getRemoveRedactionOptions = (
|
|||||||
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
|
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
|
||||||
descriptionParams: {
|
descriptionParams: {
|
||||||
value: redactions[0].value,
|
value: redactions[0].value,
|
||||||
type: redactions[0].hint ? 'hint' : redactions[0].type,
|
type: redactions[0].HINT ? 'hint' : redactions[0].type,
|
||||||
},
|
},
|
||||||
icon: PIN_ICON,
|
icon: PIN_ICON,
|
||||||
value: RemoveRedactionOptions.ONLY_HERE,
|
value: RemoveRedactionOptions.ONLY_HERE,
|
||||||
@ -169,7 +169,7 @@ export const getRemoveRedactionOptions = (
|
|||||||
options.push({
|
options.push({
|
||||||
label: isBulk ? translations.IN_DOSSIER.labelBulk : translations.IN_DOSSIER.label,
|
label: isBulk ? translations.IN_DOSSIER.labelBulk : translations.IN_DOSSIER.label,
|
||||||
description: isBulk ? translations.IN_DOSSIER.descriptionBulk : translations.IN_DOSSIER.description,
|
description: isBulk ? translations.IN_DOSSIER.descriptionBulk : translations.IN_DOSSIER.description,
|
||||||
descriptionParams: { value: redactions[0].value, type: redactions[0].hint ? 'hint' : redactions[0].type },
|
descriptionParams: { value: redactions[0].value, type: redactions[0].HINT ? 'hint' : redactions[0].type },
|
||||||
icon: FOLDER_ICON,
|
icon: FOLDER_ICON,
|
||||||
value: RemoveRedactionOptions.IN_DOSSIER,
|
value: RemoveRedactionOptions.IN_DOSSIER,
|
||||||
extraOption: !isDocumine
|
extraOption: !isDocumine
|
||||||
|
|||||||
@ -147,33 +147,26 @@ export class AnnotationDrawService {
|
|||||||
|
|
||||||
const annotation = this._pdf.textHighlight();
|
const annotation = this._pdf.textHighlight();
|
||||||
annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber);
|
annotation.Quads = this.#rectanglesToQuads(annotationWrapper.positions, pageNumber);
|
||||||
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
|
annotation.Opacity = annotationWrapper.isIgnored ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
|
||||||
annotation.setContents(annotationWrapper.content);
|
annotation.setContents(annotationWrapper.content);
|
||||||
annotation.PageNumber = pageNumber;
|
annotation.PageNumber = pageNumber;
|
||||||
annotation.StrokeColor = this.convertColor(annotationWrapper.color);
|
annotation.StrokeColor = this.convertColor(annotationWrapper.color);
|
||||||
annotation.Id = annotationWrapper.id;
|
annotation.Id = annotationWrapper.id;
|
||||||
annotation.ReadOnly = true;
|
annotation.ReadOnly = true;
|
||||||
|
|
||||||
const isOCR = annotationWrapper.isOCR && !annotationWrapper.isSuggestionResize;
|
const isOCR = annotationWrapper.isOCR;
|
||||||
if (isOCR && !this._annotationManager.isHidden(annotationWrapper.id)) {
|
if (isOCR && !this._annotationManager.isHidden(annotationWrapper.id)) {
|
||||||
this._annotationManager.addToHidden(annotationWrapper.id);
|
this._annotationManager.addToHidden(annotationWrapper.id);
|
||||||
}
|
}
|
||||||
annotation.Hidden =
|
annotation.Hidden =
|
||||||
annotationWrapper.isChangeLogRemoved ||
|
annotationWrapper.isIgnored ||
|
||||||
(hideSkipped && annotationWrapper.isSkipped) ||
|
(hideSkipped && annotationWrapper.isSkipped) ||
|
||||||
this._annotationManager.isHidden(annotationWrapper.id);
|
this._annotationManager.isHidden(annotationWrapper.id);
|
||||||
annotation.setCustomData('redact-manager', 'true');
|
annotation.setCustomData('redact-manager', 'true');
|
||||||
annotation.setCustomData('redaction', String(annotationWrapper.previewAnnotation));
|
annotation.setCustomData('redaction', String(annotationWrapper.isRedacted));
|
||||||
annotation.setCustomData('suggestion', String(annotationWrapper.isSuggestion));
|
|
||||||
annotation.setCustomData('suggestionAdd', String(annotationWrapper.isSuggestionAdd));
|
|
||||||
annotation.setCustomData('suggestionAddToFalsePositive', String(annotationWrapper.isSuggestionAddToFalsePositive));
|
|
||||||
annotation.setCustomData('suggestionRemove', String(annotationWrapper.isSuggestionRemove));
|
|
||||||
annotation.setCustomData('suggestionRecategorizeImage', String(annotationWrapper.isSuggestionRecategorizeImage));
|
|
||||||
annotation.setCustomData('suggestionForceHint', String(annotationWrapper.isSuggestionForceHint));
|
|
||||||
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
|
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
|
||||||
annotation.setCustomData('notSignatureImage', String(annotationWrapper.isNotSignatureImage));
|
|
||||||
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
|
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
|
||||||
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
|
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isIgnored));
|
||||||
annotation.setCustomData('opacity', String(annotation.Opacity));
|
annotation.setCustomData('opacity', String(annotation.Opacity));
|
||||||
|
|
||||||
const redactionColor = this._defaultColorsService.getColor(dossierTemplateId, 'previewColor');
|
const redactionColor = this._defaultColorsService.getColor(dossierTemplateId, 'previewColor');
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
import { inject, Injectable } from '@angular/core';
|
import { inject, Injectable } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { HeaderElements } from '../../file-preview/utils/constants';
|
|
||||||
import { PdfViewer } from './pdf-viewer.service';
|
|
||||||
import { REDAnnotationManager } from './annotation-manager.service';
|
|
||||||
import { AnnotationDrawService } from './annotation-draw.service';
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
|
||||||
import { Core } from '@pdftron/webviewer';
|
|
||||||
import { BASE_HREF_FN } from '@iqser/common-ui/lib/utils';
|
import { BASE_HREF_FN } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Core } from '@pdftron/webviewer';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { HeaderElements } from '../../file-preview/utils/constants';
|
||||||
|
import { AnnotationDrawService } from './annotation-draw.service';
|
||||||
|
import { PdfViewer } from './pdf-viewer.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -21,7 +20,6 @@ export class ReadableRedactionsService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
private readonly _translateService: TranslateService,
|
private readonly _translateService: TranslateService,
|
||||||
private readonly _annotationManager: REDAnnotationManager,
|
|
||||||
private readonly _annotationDrawService: AnnotationDrawService,
|
private readonly _annotationDrawService: AnnotationDrawService,
|
||||||
) {
|
) {
|
||||||
this.active$ = this.#active$.asObservable();
|
this.active$ = this.#active$.asObservable();
|
||||||
@ -79,8 +77,7 @@ export class ReadableRedactionsService {
|
|||||||
|
|
||||||
setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal = false) {
|
setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal = false) {
|
||||||
annotations.forEach(annotation => {
|
annotations.forEach(annotation => {
|
||||||
const isSuggestion = annotation.getCustomData('suggestion');
|
annotation['Opacity'] = restoreToOriginal ? parseFloat(annotation.getCustomData('opacity')) : 0.5;
|
||||||
annotation['Opacity'] = restoreToOriginal || isSuggestion ? parseFloat(annotation.getCustomData('opacity')) : 0.5;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,31 +1,20 @@
|
|||||||
import { Component, Input, OnChanges } from '@angular/core';
|
import { Component, Input, OnChanges } from '@angular/core';
|
||||||
import { SuperTypes } from '@red/domain';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { INestedFilter } from '@iqser/common-ui/lib/filtering';
|
import { INestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-type-filter [dossierTemplateId]',
|
selector: 'redaction-type-filter',
|
||||||
templateUrl: './type-filter.component.html',
|
templateUrl: './type-filter.component.html',
|
||||||
styleUrls: ['./type-filter.component.scss'],
|
styleUrls: ['./type-filter.component.scss'],
|
||||||
})
|
})
|
||||||
export class TypeFilterComponent implements OnChanges {
|
export class TypeFilterComponent implements OnChanges {
|
||||||
private _suggestionsKeys: string[] = [
|
|
||||||
SuperTypes.SuggestionRemove,
|
|
||||||
SuperTypes.SuggestionForceRedaction,
|
|
||||||
SuperTypes.SuggestionAdd,
|
|
||||||
SuperTypes.SuggestionRemoveDictionary,
|
|
||||||
SuperTypes.SuggestionAddDictionary,
|
|
||||||
SuperTypes.DeclinedSuggestion,
|
|
||||||
'suggestion',
|
|
||||||
];
|
|
||||||
private _needsAnalysisKeys: string[] = ['remove-only-here', 'analysis'];
|
private _needsAnalysisKeys: string[] = ['remove-only-here', 'analysis'];
|
||||||
@Input() filter: INestedFilter;
|
@Input() filter: INestedFilter;
|
||||||
@Input() dossierTemplateId: string;
|
@Input({ required: true }) dossierTemplateId: string;
|
||||||
@Input() dossierId: string;
|
@Input() dossierId: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
|
||||||
constructor(private readonly _defaultColorsService: DefaultColorsService, private readonly _translateService: TranslateService) {}
|
constructor(private readonly _translateService: TranslateService) {}
|
||||||
|
|
||||||
ngOnChanges(): void {
|
ngOnChanges(): void {
|
||||||
this.label =
|
this.label =
|
||||||
@ -35,8 +24,6 @@ export class TypeFilterComponent implements OnChanges {
|
|||||||
? '-'
|
? '-'
|
||||||
: this._needsAnalysisKeys.includes(this.filter.id)
|
: this._needsAnalysisKeys.includes(this.filter.id)
|
||||||
? 'A'
|
? 'A'
|
||||||
: this._suggestionsKeys.includes(this.filter.id)
|
|
||||||
? 'S'
|
|
||||||
: this.filter.skipTranslation
|
: this.filter.skipTranslation
|
||||||
? this.filter.label.charAt(0)
|
? this.filter.label.charAt(0)
|
||||||
: this._translateService.instant(this.filter.label).charAt(0);
|
: this._translateService.instant(this.filter.label).charAt(0);
|
||||||
|
|||||||
28
apps/red-ui/src/app/services/comments-api.service.ts
Normal file
28
apps/red-ui/src/app/services/comments-api.service.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable, signal } from '@angular/core';
|
||||||
|
import { GenericService } from '@common-ui/services/generic.service';
|
||||||
|
import { IComment } from '@red/domain';
|
||||||
|
import { firstValueFrom, map } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class CommentsApiService extends GenericService<IComment> {
|
||||||
|
protected readonly _defaultModelPath = 'manualRedaction';
|
||||||
|
readonly comments = signal<Record<string, IComment[]>>({});
|
||||||
|
|
||||||
|
async add(comment: string, annotationId: string, dossierId: string, fileId: string) {
|
||||||
|
const url = `${this._defaultModelPath}/comment/add/${dossierId}/${fileId}/${annotationId}`;
|
||||||
|
const request = await firstValueFrom(this._post<{ commentId: string }>({ text: comment }, url));
|
||||||
|
return request.commentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(commentId: string, annotationId: string, dossierId: string, fileId: string) {
|
||||||
|
const url = `${this._defaultModelPath}/comment/undo/${dossierId}/${fileId}/${annotationId}/${commentId}`;
|
||||||
|
return firstValueFrom(super.delete({}, url));
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(dossierId: string, fileId: string, annotationId: string): Promise<IComment[]> {
|
||||||
|
const url = `${this._defaultModelPath}/comments/${dossierId}/${fileId}/${annotationId}`;
|
||||||
|
return firstValueFrom(super.getAll<{ comments: IComment[] }>(url).pipe(map(res => res.comments)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { GenericService, QueryParam } from '@iqser/common-ui';
|
import { GenericService } from '@iqser/common-ui';
|
||||||
import { IRedactionLog, ISectionGrid } from '@red/domain';
|
import { IEntityLog, ISectionGrid } from '@red/domain';
|
||||||
import { catchError } from 'rxjs/operators';
|
|
||||||
import { firstValueFrom, of } from 'rxjs';
|
import { firstValueFrom, of } from 'rxjs';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -10,16 +10,11 @@ import { firstValueFrom, of } from 'rxjs';
|
|||||||
export class RedactionLogService extends GenericService<unknown> {
|
export class RedactionLogService extends GenericService<unknown> {
|
||||||
protected readonly _defaultModelPath = '';
|
protected readonly _defaultModelPath = '';
|
||||||
|
|
||||||
async getRedactionLog(dossierId: string, fileId: string, withManualRedactions?: boolean) {
|
async getEntityLog(dossierId: string, fileId: string) {
|
||||||
const queryParams: QueryParam[] = [];
|
const entityLog$ = this._getOne<IEntityLog>([dossierId, fileId], 'entityLog');
|
||||||
if (withManualRedactions) {
|
const entityLog = await firstValueFrom(entityLog$.pipe(catchError(() => of({} as IEntityLog))));
|
||||||
queryParams.push({ key: 'withManualRedactions', value: withManualRedactions });
|
entityLog.entityLogEntry.sort((a, b) => a.positions[0].pageNumber - b.positions[0].pageNumber);
|
||||||
}
|
return entityLog;
|
||||||
|
|
||||||
const redactionLog$ = this._getOne<IRedactionLog>([dossierId, fileId], 'redactionLog', queryParams);
|
|
||||||
const redactionLog = await firstValueFrom(redactionLog$.pipe(catchError(() => of({} as IRedactionLog))));
|
|
||||||
redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page);
|
|
||||||
return redactionLog;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSectionGrid(dossierId: string, fileId: string) {
|
getSectionGrid(dossierId: string, fileId: string) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { DictionaryActions, ManualRedactionActions } from '@red/domain';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { DictionaryActions, ManualRedactionActions } from '@red/domain';
|
||||||
|
|
||||||
interface AnnotationActionResponses {
|
interface AnnotationActionResponses {
|
||||||
error: string;
|
error: string;
|
||||||
@ -13,26 +13,10 @@ export const dictionaryActionsTranslations: Record<DictionaryActions, Annotation
|
|||||||
conflictError: _('annotation-actions.message.dictionary.add.conflict-error'),
|
conflictError: _('annotation-actions.message.dictionary.add.conflict-error'),
|
||||||
success: _('annotation-actions.message.dictionary.add.success'),
|
success: _('annotation-actions.message.dictionary.add.success'),
|
||||||
},
|
},
|
||||||
approve: {
|
|
||||||
error: _('annotation-actions.message.dictionary.approve.error'),
|
|
||||||
success: _('annotation-actions.message.dictionary.approve.success'),
|
|
||||||
},
|
|
||||||
decline: {
|
|
||||||
error: _('annotation-actions.message.dictionary.decline.error'),
|
|
||||||
success: _('annotation-actions.message.dictionary.decline.success'),
|
|
||||||
},
|
|
||||||
remove: {
|
remove: {
|
||||||
error: _('annotation-actions.message.dictionary.remove.error'),
|
error: _('annotation-actions.message.dictionary.remove.error'),
|
||||||
success: _('annotation-actions.message.dictionary.remove.success'),
|
success: _('annotation-actions.message.dictionary.remove.success'),
|
||||||
},
|
},
|
||||||
'request-remove': {
|
|
||||||
error: _('annotation-actions.message.dictionary.request-remove.error'),
|
|
||||||
success: _('annotation-actions.message.dictionary.request-remove.success'),
|
|
||||||
},
|
|
||||||
suggest: {
|
|
||||||
error: _('annotation-actions.message.dictionary.suggest.error'),
|
|
||||||
success: _('annotation-actions.message.dictionary.suggest.success'),
|
|
||||||
},
|
|
||||||
undo: {
|
undo: {
|
||||||
error: _('annotation-actions.message.dictionary.undo.error'),
|
error: _('annotation-actions.message.dictionary.undo.error'),
|
||||||
success: _('annotation-actions.message.dictionary.undo.success'),
|
success: _('annotation-actions.message.dictionary.undo.success'),
|
||||||
@ -44,18 +28,10 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
|||||||
error: _('annotation-actions.message.manual-redaction.add.error'),
|
error: _('annotation-actions.message.manual-redaction.add.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.add.success'),
|
success: _('annotation-actions.message.manual-redaction.add.success'),
|
||||||
},
|
},
|
||||||
approve: {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.approve.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.approve.success'),
|
|
||||||
},
|
|
||||||
'change-legal-basis': {
|
'change-legal-basis': {
|
||||||
error: _('annotation-actions.message.manual-redaction.change-legal-basis.error'),
|
error: _('annotation-actions.message.manual-redaction.change-legal-basis.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.change-legal-basis.success'),
|
success: _('annotation-actions.message.manual-redaction.change-legal-basis.success'),
|
||||||
},
|
},
|
||||||
decline: {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.decline.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.decline.success'),
|
|
||||||
},
|
|
||||||
'force-redaction': {
|
'force-redaction': {
|
||||||
error: _('annotation-actions.message.manual-redaction.force-redaction.error'),
|
error: _('annotation-actions.message.manual-redaction.force-redaction.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.force-redaction.success'),
|
success: _('annotation-actions.message.manual-redaction.force-redaction.success'),
|
||||||
@ -72,26 +48,6 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
|||||||
error: _('annotation-actions.message.manual-redaction.change-type.error'),
|
error: _('annotation-actions.message.manual-redaction.change-type.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.change-type.success'),
|
success: _('annotation-actions.message.manual-redaction.change-type.success'),
|
||||||
},
|
},
|
||||||
'request-change-legal-basis': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-change-legal-basis.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-change-legal-basis.success'),
|
|
||||||
},
|
|
||||||
'request-force-redaction': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-force-redaction.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-force-redaction.success'),
|
|
||||||
},
|
|
||||||
'request-force-hint': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-force-hint.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-force-hint.success'),
|
|
||||||
},
|
|
||||||
'request-image-recategorization': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-image-recategorization.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-image-recategorization.success'),
|
|
||||||
},
|
|
||||||
suggest: {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.suggest.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.suggest.success'),
|
|
||||||
},
|
|
||||||
undo: {
|
undo: {
|
||||||
error: _('annotation-actions.message.manual-redaction.undo.error'),
|
error: _('annotation-actions.message.manual-redaction.undo.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.undo.success'),
|
success: _('annotation-actions.message.manual-redaction.undo.success'),
|
||||||
@ -104,12 +60,4 @@ export const manualRedactionActionsTranslations: Record<ManualRedactionActions,
|
|||||||
error: _('annotation-actions.message.manual-redaction.remove-hint.error'),
|
error: _('annotation-actions.message.manual-redaction.remove-hint.error'),
|
||||||
success: _('annotation-actions.message.manual-redaction.remove-hint.success'),
|
success: _('annotation-actions.message.manual-redaction.remove-hint.success'),
|
||||||
},
|
},
|
||||||
'request-remove': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-remove.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-remove.success'),
|
|
||||||
},
|
|
||||||
'request-remove-hint': {
|
|
||||||
error: _('annotation-actions.message.manual-redaction.request-remove-hint.error'),
|
|
||||||
success: _('annotation-actions.message.manual-redaction.request-remove-hint.success'),
|
|
||||||
},
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -1,27 +1,15 @@
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { SuperType, SuperTypes } from '@red/domain';
|
import { SuperType, SuperTypes } from '@red/domain';
|
||||||
|
|
||||||
export const SuggestionAddFalsePositive = 'suggestion-add-false-positive' as const;
|
type TranslationKey = SuperType;
|
||||||
|
|
||||||
type TranslationKey = SuperType & typeof SuggestionAddFalsePositive;
|
|
||||||
|
|
||||||
export const annotationTypesTranslations: Record<TranslationKey, string> = {
|
export const annotationTypesTranslations: Record<TranslationKey, string> = {
|
||||||
[SuperTypes.TextHighlight]: _('annotation-type.text-highlight'),
|
[SuperTypes.TextHighlight]: _('annotation-type.text-highlight'),
|
||||||
[SuperTypes.DeclinedSuggestion]: _('annotation-type.declined-suggestion'),
|
|
||||||
[SuperTypes.Hint]: _('annotation-type.hint'),
|
[SuperTypes.Hint]: _('annotation-type.hint'),
|
||||||
[SuperTypes.IgnoredHint]: _('annotation-type.ignored-hint'),
|
[SuperTypes.IgnoredHint]: _('annotation-type.ignored-hint'),
|
||||||
[SuperTypes.ManualRedaction]: _('annotation-type.manual-redaction'),
|
[SuperTypes.ManualRedaction]: _('annotation-type.manual-redaction'),
|
||||||
|
[SuperTypes.ManualHint]: _('annotation-type.manual-hint'),
|
||||||
[SuperTypes.Recommendation]: _('annotation-type.recommendation'),
|
[SuperTypes.Recommendation]: _('annotation-type.recommendation'),
|
||||||
[SuperTypes.Redaction]: _('annotation-type.redaction'),
|
[SuperTypes.Redaction]: _('annotation-type.redaction'),
|
||||||
[SuperTypes.Skipped]: _('annotation-type.skipped'),
|
[SuperTypes.Skipped]: _('annotation-type.skipped'),
|
||||||
[SuperTypes.SuggestionAdd]: _('annotation-type.suggestion-add'),
|
|
||||||
[SuperTypes.SuggestionAddDictionary]: _('annotation-type.suggestion-add-dictionary'),
|
|
||||||
[SuperTypes.SuggestionChangeLegalBasis]: _('annotation-type.suggestion-change-legal-basis'),
|
|
||||||
[SuperTypes.SuggestionRecategorizeImage]: _('annotation-type.suggestion-recategorize-image'),
|
|
||||||
[SuperTypes.SuggestionForceRedaction]: _('annotation-type.suggestion-force-redaction'),
|
|
||||||
[SuperTypes.SuggestionForceHint]: _('annotation-type.suggestion-force-hint'),
|
|
||||||
[SuperTypes.SuggestionRemove]: _('annotation-type.suggestion-remove'),
|
|
||||||
[SuperTypes.SuggestionRemoveDictionary]: _('annotation-type.suggestion-remove-dictionary'),
|
|
||||||
[SuperTypes.SuggestionResize]: _('annotation-type.suggestion-resize'),
|
|
||||||
[SuggestionAddFalsePositive]: _('annotation-type.suggestion-add-false-positive'),
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -6,7 +6,6 @@ export const workloadTranslations: { [key: string]: string } = {
|
|||||||
image: _('filter.image'),
|
image: _('filter.image'),
|
||||||
none: _('filter.none'),
|
none: _('filter.none'),
|
||||||
redaction: _('filter.redaction'),
|
redaction: _('filter.redaction'),
|
||||||
suggestion: _('filter.suggestion'),
|
|
||||||
updated: _('filter.updated'),
|
updated: _('filter.updated'),
|
||||||
comment: _('filter.comment'),
|
comment: _('filter.comment'),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,7 +7,6 @@ export const PreferencesKeys = {
|
|||||||
lastDossierTemplate: 'Last-Dossier-Template',
|
lastDossierTemplate: 'Last-Dossier-Template',
|
||||||
filesListingMode: 'Files-Listing-Mode',
|
filesListingMode: 'Files-Listing-Mode',
|
||||||
autoExpandFiltersOnActions: 'Auto-Expand-Filters-On-Actions',
|
autoExpandFiltersOnActions: 'Auto-Expand-Filters-On-Actions',
|
||||||
displaySuggestionsInPreview: 'Display-Suggestions-In-Preview',
|
|
||||||
loadAllAnnotationsWarning: 'Load-All-Annotations-Warning',
|
loadAllAnnotationsWarning: 'Load-All-Annotations-Warning',
|
||||||
openScmDialogByDefault: 'Open-Structured-Component-Management-By-Default',
|
openScmDialogByDefault: 'Open-Structured-Component-Management-By-Default',
|
||||||
tableExtractionType: 'Table-Extraction-Type',
|
tableExtractionType: 'Table-Extraction-Type',
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Dossier, File, User, UserType } from '@red/domain';
|
|
||||||
import { handleCheckedValue, INestedFilter } from '@iqser/common-ui/lib/filtering';
|
import { handleCheckedValue, INestedFilter } from '@iqser/common-ui/lib/filtering';
|
||||||
|
import { Dossier, File, User, UserType } from '@red/domain';
|
||||||
|
|
||||||
export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) {
|
export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INestedFilter[], allFilters: INestedFilter[]) {
|
||||||
const newFiltersDelta = {};
|
const newFiltersDelta = {};
|
||||||
@ -52,9 +52,6 @@ export const annotationFilterChecker = (file: File, filter: INestedFilter, curre
|
|||||||
case 'analysis': {
|
case 'analysis': {
|
||||||
return file.analysisRequired;
|
return file.analysisRequired;
|
||||||
}
|
}
|
||||||
case 'suggestion': {
|
|
||||||
return file.hasSuggestions;
|
|
||||||
}
|
|
||||||
case 'redaction': {
|
case 'redaction': {
|
||||||
return file.hasRedactions;
|
return file.hasRedactions;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ export const RedactionFilterSorter = {
|
|||||||
redaction: 2,
|
redaction: 2,
|
||||||
image: 3,
|
image: 3,
|
||||||
hint: 4,
|
hint: 4,
|
||||||
suggestion: 5,
|
|
||||||
none: 6,
|
none: 6,
|
||||||
byKey: (a: { id: string }, b: { id: string }): number => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id],
|
byKey: (a: { id: string }, b: { id: string }): number => RedactionFilterSorter[a.id] - RedactionFilterSorter[b.id],
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,20 +2,11 @@ import { SuperType, SuperTypes } from '@red/domain';
|
|||||||
|
|
||||||
export const SuperTypeSorter: { [key in SuperType]: number } = {
|
export const SuperTypeSorter: { [key in SuperType]: number } = {
|
||||||
[SuperTypes.TextHighlight]: 100,
|
[SuperTypes.TextHighlight]: 100,
|
||||||
[SuperTypes.SuggestionChangeLegalBasis]: 14,
|
|
||||||
[SuperTypes.SuggestionForceRedaction]: 15,
|
|
||||||
[SuperTypes.SuggestionForceHint]: 16,
|
|
||||||
[SuperTypes.SuggestionRecategorizeImage]: 17,
|
|
||||||
[SuperTypes.SuggestionResize]: 18,
|
|
||||||
[SuperTypes.Recommendation]: 7,
|
[SuperTypes.Recommendation]: 7,
|
||||||
[SuperTypes.SuggestionAddDictionary]: 12,
|
|
||||||
[SuperTypes.SuggestionRemoveDictionary]: 13,
|
|
||||||
[SuperTypes.SuggestionAdd]: 10,
|
|
||||||
[SuperTypes.SuggestionRemove]: 11,
|
|
||||||
[SuperTypes.IgnoredHint]: 45,
|
[SuperTypes.IgnoredHint]: 45,
|
||||||
[SuperTypes.Skipped]: 50,
|
[SuperTypes.Skipped]: 50,
|
||||||
[SuperTypes.Redaction]: 1,
|
[SuperTypes.Redaction]: 1,
|
||||||
[SuperTypes.ManualRedaction]: 2,
|
[SuperTypes.ManualRedaction]: 2,
|
||||||
|
[SuperTypes.ManualHint]: 3,
|
||||||
[SuperTypes.Hint]: 19,
|
[SuperTypes.Hint]: 19,
|
||||||
[SuperTypes.DeclinedSuggestion]: 20,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ADMIN_CONTACT_NAME": null,
|
"ADMIN_CONTACT_NAME": null,
|
||||||
"ADMIN_CONTACT_URL": null,
|
"ADMIN_CONTACT_URL": null,
|
||||||
"API_URL": "https://dan.iqser.cloud",
|
"API_URL": "https://dan1.iqser.cloud",
|
||||||
"APP_NAME": "RedactManager",
|
"APP_NAME": "RedactManager",
|
||||||
"IS_DOCUMINE": false,
|
"IS_DOCUMINE": false,
|
||||||
"RULE_EDITOR_DEV_ONLY": false,
|
"RULE_EDITOR_DEV_ONLY": false,
|
||||||
@ -13,7 +13,7 @@
|
|||||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||||
"OAUTH_CLIENT_ID": "redaction",
|
"OAUTH_CLIENT_ID": "redaction",
|
||||||
"OAUTH_IDP_HINT": null,
|
"OAUTH_IDP_HINT": null,
|
||||||
"OAUTH_URL": "https://dan.iqser.cloud/auth",
|
"OAUTH_URL": "https://dan1.iqser.cloud/auth",
|
||||||
"RECENT_PERIOD_IN_HOURS": 24,
|
"RECENT_PERIOD_IN_HOURS": 24,
|
||||||
"SELECTION_MODE": "structural",
|
"SELECTION_MODE": "structural",
|
||||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"add-to-dictionary": "Zum Wörterbuch hinzufügen",
|
"add-to-dictionary": "Zum Wörterbuch hinzufügen",
|
||||||
"request-add-to-dictionary": "Request add to dictionary"
|
"request-add-to-dictionary": "Request add to dictionary"
|
||||||
},
|
}
|
||||||
"multiple-values": "Multiple recommendations selected"
|
|
||||||
},
|
},
|
||||||
"account-settings": "Kontoeinstellungen",
|
"account-settings": "Kontoeinstellungen",
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -56,8 +55,7 @@
|
|||||||
},
|
},
|
||||||
"add-edit-clone-dossier-template": {
|
"add-edit-clone-dossier-template": {
|
||||||
"error": {
|
"error": {
|
||||||
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen.",
|
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen."
|
||||||
"generic": "Fehler beim Erstellen der Dossiervorlage."
|
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"apply-updates-default": {
|
"apply-updates-default": {
|
||||||
@ -256,9 +254,6 @@
|
|||||||
"user-management": "User Management",
|
"user-management": "User Management",
|
||||||
"watermarks": "Watermarks"
|
"watermarks": "Watermarks"
|
||||||
},
|
},
|
||||||
"annotation": {
|
|
||||||
"pending": "(Pending Analysis)"
|
|
||||||
},
|
|
||||||
"annotation-actions": {
|
"annotation-actions": {
|
||||||
"accept-recommendation": {
|
"accept-recommendation": {
|
||||||
"label": "Empfehlung annehmen"
|
"label": "Empfehlung annehmen"
|
||||||
@ -283,26 +278,10 @@
|
|||||||
"error": "Fehler beim Hinzufügen des neuen Wörterbucheintrags: {error}",
|
"error": "Fehler beim Hinzufügen des neuen Wörterbucheintrags: {error}",
|
||||||
"success": "Eintrag zum Wörterbuch hinzugefügt. Änderungen nach der Reanalyse sichtbar."
|
"success": "Eintrag zum Wörterbuch hinzugefügt. Änderungen nach der Reanalyse sichtbar."
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Fehler beim Genehmigen des Wörterbucheintrags: {error}",
|
|
||||||
"success": "Neuer Wörterbucheintrag wurde genehmigt. Änderungen sind nach der Reanalyse sichtbar."
|
|
||||||
},
|
|
||||||
"decline": {
|
|
||||||
"error": "Fehler beim Ablehnen des neuen Wörterbucheintrags: {error}",
|
|
||||||
"success": "Vorschlag für das Wörterbuch abgelehnt."
|
|
||||||
},
|
|
||||||
"remove": {
|
"remove": {
|
||||||
"error": "Fehler beim Entfernen des Wörterbucheintrags: {error}",
|
"error": "Fehler beim Entfernen des Wörterbucheintrags: {error}",
|
||||||
"success": "Wörterbucheintrag wurde gelöscht!"
|
"success": "Wörterbucheintrag wurde gelöscht!"
|
||||||
},
|
},
|
||||||
"request-remove": {
|
|
||||||
"error": "Dossier-Vorlage",
|
|
||||||
"success": "Löschung des Wörterbucheintrags wurde vorgeschlagen!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Vorschlag für Änderung des Wörterbuchs konnte nicht gespeichert werden: {error}",
|
|
||||||
"success": "Vorschlag für die Änderung des Wörterbuchs gespeichert!"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
"success": "Rückgängigmachen erfolgreich"
|
"success": "Rückgängigmachen erfolgreich"
|
||||||
@ -313,10 +292,6 @@
|
|||||||
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
||||||
"success": "Schwärzung hinzugefügt!"
|
"success": "Schwärzung hinzugefügt!"
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Fehler beim Genehmigen des Vorschlags: {error}",
|
|
||||||
"success": "Vorschlag genehmigt"
|
|
||||||
},
|
|
||||||
"change-legal-basis": {
|
"change-legal-basis": {
|
||||||
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
||||||
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
||||||
@ -325,10 +300,6 @@
|
|||||||
"error": "Failed to edit type: {error}",
|
"error": "Failed to edit type: {error}",
|
||||||
"success": "Type was edited."
|
"success": "Type was edited."
|
||||||
},
|
},
|
||||||
"decline": {
|
|
||||||
"error": "Fehler beim Ablehnen des Vorschlags: {error}",
|
|
||||||
"success": "Vorschlag abgelehnt"
|
|
||||||
},
|
|
||||||
"force-hint": {
|
"force-hint": {
|
||||||
"error": "Failed to save hint: {error}",
|
"error": "Failed to save hint: {error}",
|
||||||
"success": "Hint added!"
|
"success": "Hint added!"
|
||||||
@ -341,41 +312,13 @@
|
|||||||
"error": "Rekategorisierung des Bildes gescheitert: {error}",
|
"error": "Rekategorisierung des Bildes gescheitert: {error}",
|
||||||
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
"success": "Bild wurde einer neuen Kategorie zugeordnet."
|
||||||
},
|
},
|
||||||
"remove": {
|
|
||||||
"error": "Fehler beim Entfernen der Schwärzung: {error}",
|
|
||||||
"success": "Schwärzung entfernt!"
|
|
||||||
},
|
|
||||||
"remove-hint": {
|
"remove-hint": {
|
||||||
"error": "Failed to remove hint: {error}",
|
"error": "Failed to remove hint: {error}",
|
||||||
"success": "Hint removed!"
|
"success": "Hint removed!"
|
||||||
},
|
},
|
||||||
"request-change-legal-basis": {
|
"remove": {
|
||||||
"error": "Fehler beim Vorschlagen der Änderung der Begründung:",
|
"error": "Fehler beim Entfernen der Schwärzung: {error}",
|
||||||
"success": "Die Änderung der in der Anmerkung genannten Begründung wurde beantragt."
|
"success": "Schwärzung entfernt!"
|
||||||
},
|
|
||||||
"request-force-hint": {
|
|
||||||
"error": "Failed to save hint suggestion: {error}",
|
|
||||||
"success": "Hint suggestion saved"
|
|
||||||
},
|
|
||||||
"request-force-redaction": {
|
|
||||||
"error": "Fehler beim Speichern des Schwärzungsvorschlags: {error}",
|
|
||||||
"success": "Vorschlag einer Schwärzung gespeichert"
|
|
||||||
},
|
|
||||||
"request-image-recategorization": {
|
|
||||||
"error": "Fehler beim Vorschlagen der Neukategorisierung des Bilds: {error}",
|
|
||||||
"success": "Bild-Neuklassifizierung angefordert."
|
|
||||||
},
|
|
||||||
"request-remove": {
|
|
||||||
"error": "Fehler beim Erstellen des Vorschlags für das Entfernen der Schwärzung: {error}",
|
|
||||||
"success": "Entfernen der Schwärzung wurde vorgeschlagen!"
|
|
||||||
},
|
|
||||||
"request-remove-hint": {
|
|
||||||
"error": "Failed to request removal of hint: {error}",
|
|
||||||
"success": "Requested to remove hint!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Vorschlag einer Schwärzung wurde nicht gespeichert: {error}",
|
|
||||||
"success": "Vorschlag einer Schwärzung gespeichert"
|
|
||||||
},
|
},
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
@ -389,15 +332,15 @@
|
|||||||
"remove-highlights": {
|
"remove-highlights": {
|
||||||
"label": "Remove Selected Earmarks"
|
"label": "Remove Selected Earmarks"
|
||||||
},
|
},
|
||||||
"resize": {
|
|
||||||
"label": "Größe ändern"
|
|
||||||
},
|
|
||||||
"resize-accept": {
|
"resize-accept": {
|
||||||
"label": "Größe speichern"
|
"label": "Größe speichern"
|
||||||
},
|
},
|
||||||
"resize-cancel": {
|
"resize-cancel": {
|
||||||
"label": "Größenänderung abbrechen"
|
"label": "Größenänderung abbrechen"
|
||||||
},
|
},
|
||||||
|
"resize": {
|
||||||
|
"label": "Größe ändern"
|
||||||
|
},
|
||||||
"see-references": {
|
"see-references": {
|
||||||
"label": "See References"
|
"label": "See References"
|
||||||
},
|
},
|
||||||
@ -419,7 +362,6 @@
|
|||||||
"rule": "Schwärzung basierend auf Regel {rule}"
|
"rule": "Schwärzung basierend auf Regel {rule}"
|
||||||
},
|
},
|
||||||
"annotation-type": {
|
"annotation-type": {
|
||||||
"declined-suggestion": "Abgelehnter Vorschlag",
|
|
||||||
"hint": "Hinweis",
|
"hint": "Hinweis",
|
||||||
"ignored-hint": "Ignorierter Hinweis",
|
"ignored-hint": "Ignorierter Hinweis",
|
||||||
"manual-hint": "Manual Hint",
|
"manual-hint": "Manual Hint",
|
||||||
@ -427,18 +369,11 @@
|
|||||||
"recommendation": "Empfehlung",
|
"recommendation": "Empfehlung",
|
||||||
"redaction": "Schwärzung",
|
"redaction": "Schwärzung",
|
||||||
"skipped": "Übersprungen",
|
"skipped": "Übersprungen",
|
||||||
"suggestion-add": "Vorschlag für Schwärzung",
|
|
||||||
"suggestion-add-dictionary": "Vorschlag für neuen Wörterbucheintrag",
|
|
||||||
"suggestion-add-false-positive": "Suggested add to false positive",
|
|
||||||
"suggestion-change-legal-basis": "Vorschlag für Änderung der Rechtsgrundlage",
|
|
||||||
"suggestion-force-hint": "Suggestion force hint",
|
|
||||||
"suggestion-force-redaction": "Vorschlag für erzwungene Schwärzung",
|
|
||||||
"suggestion-recategorize-image": "Vorschlag für Rekategorisierung eines Bilds",
|
|
||||||
"suggestion-remove": "Vorschlagen, die Schwärzung zu entfernen",
|
|
||||||
"suggestion-remove-dictionary": "Vorschlag für Löschung eines Wörterbucheintrags",
|
|
||||||
"suggestion-resize": "Vorgeschlagene Größenänderung",
|
|
||||||
"text-highlight": "Earmark"
|
"text-highlight": "Earmark"
|
||||||
},
|
},
|
||||||
|
"annotation": {
|
||||||
|
"pending": "(Pending Analysis)"
|
||||||
|
},
|
||||||
"archived-dossiers-listing": {
|
"archived-dossiers-listing": {
|
||||||
"no-data": {
|
"no-data": {
|
||||||
"title": "No archived dossiers."
|
"title": "No archived dossiers."
|
||||||
@ -638,18 +573,14 @@
|
|||||||
"warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!"
|
"warning": "Achtung: Diese Aktion kann nicht rückgängig gemacht werden!"
|
||||||
},
|
},
|
||||||
"confirmation-dialog": {
|
"confirmation-dialog": {
|
||||||
"approve-file": {
|
|
||||||
"question": "Dieses Dokument enthält ungesehene Änderungen. Möchten Sie es trotzdem genehmigen?",
|
|
||||||
"title": "Warnung!"
|
|
||||||
},
|
|
||||||
"approve-file-without-analysis": {
|
"approve-file-without-analysis": {
|
||||||
"confirmationText": "Approve without analysis",
|
"confirmationText": "Approve without analysis",
|
||||||
"denyText": "Cancel",
|
"denyText": "Cancel",
|
||||||
"question": "Analysis required to detect new redactions.",
|
"question": "Analysis required to detect new redactions.",
|
||||||
"title": "Warning!"
|
"title": "Warning!"
|
||||||
},
|
},
|
||||||
"approve-multiple-files": {
|
"approve-file": {
|
||||||
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen. Möchten Sie sie trotzdem genehmigen?",
|
"question": "Dieses Dokument enthält ungesehene Änderungen. Möchten Sie es trotzdem genehmigen?",
|
||||||
"title": "Warnung!"
|
"title": "Warnung!"
|
||||||
},
|
},
|
||||||
"approve-multiple-files-without-analysis": {
|
"approve-multiple-files-without-analysis": {
|
||||||
@ -658,6 +589,10 @@
|
|||||||
"question": "Analysis required to detect new redactions for at least one file.",
|
"question": "Analysis required to detect new redactions for at least one file.",
|
||||||
"title": "Warning"
|
"title": "Warning"
|
||||||
},
|
},
|
||||||
|
"approve-multiple-files": {
|
||||||
|
"question": "Mindestens eine der ausgewählten Dateien enthält ungesehene Änderungen. Möchten Sie sie trotzdem genehmigen?",
|
||||||
|
"title": "Warnung!"
|
||||||
|
},
|
||||||
"assign-file-to-me": {
|
"assign-file-to-me": {
|
||||||
"question": {
|
"question": {
|
||||||
"multiple": "Dieses Dokument wird gerade von einer anderen Person geprüft. Möchten Sie Reviewer werden und sich selbst dem Dokument zuweisen?",
|
"multiple": "Dieses Dokument wird gerade von einer anderen Person geprüft. Möchten Sie Reviewer werden und sich selbst dem Dokument zuweisen?",
|
||||||
@ -1003,13 +938,13 @@
|
|||||||
"recent": "Neu ({hours} h)",
|
"recent": "Neu ({hours} h)",
|
||||||
"unassigned": "Niemandem zugewiesen"
|
"unassigned": "Niemandem zugewiesen"
|
||||||
},
|
},
|
||||||
"reanalyse": {
|
|
||||||
"action": "Datei analysieren"
|
|
||||||
},
|
|
||||||
"reanalyse-dossier": {
|
"reanalyse-dossier": {
|
||||||
"error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.",
|
"error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.",
|
||||||
"success": "Dateien für Reanalyse vorgesehen."
|
"success": "Dateien für Reanalyse vorgesehen."
|
||||||
},
|
},
|
||||||
|
"reanalyse": {
|
||||||
|
"action": "Datei analysieren"
|
||||||
|
},
|
||||||
"start-auto-analysis": "Enable auto-analysis",
|
"start-auto-analysis": "Enable auto-analysis",
|
||||||
"stop-auto-analysis": "Stop auto-analysis",
|
"stop-auto-analysis": "Stop auto-analysis",
|
||||||
"table-col-names": {
|
"table-col-names": {
|
||||||
@ -1078,14 +1013,6 @@
|
|||||||
"total-documents": "Anzahl der Dokumente",
|
"total-documents": "Anzahl der Dokumente",
|
||||||
"total-people": "<strong>{count}</strong> {count, plural, one{User} other {Users}}"
|
"total-people": "<strong>{count}</strong> {count, plural, one{User} other {Users}}"
|
||||||
},
|
},
|
||||||
"dossier-templates": {
|
|
||||||
"label": "Dossier-Vorlagen",
|
|
||||||
"status": {
|
|
||||||
"active": "Active",
|
|
||||||
"inactive": "Inactive",
|
|
||||||
"incomplete": "Incomplete"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dossier-templates-listing": {
|
"dossier-templates-listing": {
|
||||||
"action": {
|
"action": {
|
||||||
"clone": "Clone Template",
|
"clone": "Clone Template",
|
||||||
@ -1121,6 +1048,14 @@
|
|||||||
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
|
"title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dossier-templates": {
|
||||||
|
"label": "Dossier-Vorlagen",
|
||||||
|
"status": {
|
||||||
|
"active": "Active",
|
||||||
|
"inactive": "Inactive",
|
||||||
|
"incomplete": "Incomplete"
|
||||||
|
}
|
||||||
|
},
|
||||||
"dossier-watermark-selector": {
|
"dossier-watermark-selector": {
|
||||||
"heading": "Watermarks on documents",
|
"heading": "Watermarks on documents",
|
||||||
"no-watermark": "There is no watermark defined for the dossier template.<br>Contact your app admin to define one.",
|
"no-watermark": "There is no watermark defined for the dossier template.<br>Contact your app admin to define one.",
|
||||||
@ -1304,15 +1239,6 @@
|
|||||||
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
|
"title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"entity": {
|
|
||||||
"info": {
|
|
||||||
"actions": {
|
|
||||||
"revert": "Revert",
|
|
||||||
"save": "Save Changes"
|
|
||||||
},
|
|
||||||
"heading": "Edit Entity"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"entity-rules-screen": {
|
"entity-rules-screen": {
|
||||||
"error": {
|
"error": {
|
||||||
"generic": "Something went wrong... Entity rules update failed!"
|
"generic": "Something went wrong... Entity rules update failed!"
|
||||||
@ -1326,19 +1252,28 @@
|
|||||||
"title": "Entity Rule Editor",
|
"title": "Entity Rule Editor",
|
||||||
"warning-text": "Warning: experimental feature!"
|
"warning-text": "Warning: experimental feature!"
|
||||||
},
|
},
|
||||||
|
"entity": {
|
||||||
|
"info": {
|
||||||
|
"actions": {
|
||||||
|
"revert": "Revert",
|
||||||
|
"save": "Save Changes"
|
||||||
|
},
|
||||||
|
"heading": "Edit Entity"
|
||||||
|
}
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"deleted-entity": {
|
"deleted-entity": {
|
||||||
"dossier": {
|
"dossier": {
|
||||||
"action": "Zurück zur Übersicht",
|
"action": "Zurück zur Übersicht",
|
||||||
"label": "Dieses Dossier wurde gelöscht!"
|
"label": "Dieses Dossier wurde gelöscht!"
|
||||||
},
|
},
|
||||||
"file": {
|
|
||||||
"action": "Zurück zum Dossier",
|
|
||||||
"label": "Diese Datei wurde gelöscht!"
|
|
||||||
},
|
|
||||||
"file-dossier": {
|
"file-dossier": {
|
||||||
"action": "Zurück zur Übersicht",
|
"action": "Zurück zur Übersicht",
|
||||||
"label": "Das Dossier dieser Datei wurde gelöscht!"
|
"label": "Das Dossier dieser Datei wurde gelöscht!"
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"action": "Zurück zum Dossier",
|
||||||
|
"label": "Diese Datei wurde gelöscht!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"file-preview": {
|
"file-preview": {
|
||||||
@ -1356,12 +1291,6 @@
|
|||||||
},
|
},
|
||||||
"exact-date": "{day} {month} {year} um {hour}:{minute} Uhr",
|
"exact-date": "{day} {month} {year} um {hour}:{minute} Uhr",
|
||||||
"file": "Datei",
|
"file": "Datei",
|
||||||
"file-attribute": {
|
|
||||||
"update": {
|
|
||||||
"error": "Failed to update file attribute value!",
|
|
||||||
"success": "File attribute value has been updated successfully!"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"file-attribute-encoding-types": {
|
"file-attribute-encoding-types": {
|
||||||
"ascii": "ASCII",
|
"ascii": "ASCII",
|
||||||
"iso": "ISO-8859-1",
|
"iso": "ISO-8859-1",
|
||||||
@ -1372,6 +1301,12 @@
|
|||||||
"number": "Nummer",
|
"number": "Nummer",
|
||||||
"text": "Freier Text"
|
"text": "Freier Text"
|
||||||
},
|
},
|
||||||
|
"file-attribute": {
|
||||||
|
"update": {
|
||||||
|
"error": "Failed to update file attribute value!",
|
||||||
|
"success": "File attribute value has been updated successfully!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"file-attributes-configurations": {
|
"file-attributes-configurations": {
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"form": {
|
"form": {
|
||||||
@ -1585,16 +1520,6 @@
|
|||||||
"csv": "File attributes were imported successfully from uploaded CSV file."
|
"csv": "File attributes were imported successfully from uploaded CSV file."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filter": {
|
|
||||||
"analysis": "Analyse erforderlich",
|
|
||||||
"comment": "Kommentare",
|
|
||||||
"hint": "Nut Hinweise",
|
|
||||||
"image": "Bilder",
|
|
||||||
"none": "Keine Anmerkungen",
|
|
||||||
"redaction": "Geschwärzt",
|
|
||||||
"suggestion": "Vorgeschlagene Schwärzung",
|
|
||||||
"updated": "Aktualisiert"
|
|
||||||
},
|
|
||||||
"filter-menu": {
|
"filter-menu": {
|
||||||
"filter-options": "Filteroptionen",
|
"filter-options": "Filteroptionen",
|
||||||
"filter-types": "Filter",
|
"filter-types": "Filter",
|
||||||
@ -1604,6 +1529,15 @@
|
|||||||
"unseen-pages": "Nur Anmerkungen auf unsichtbaren Seiten",
|
"unseen-pages": "Nur Anmerkungen auf unsichtbaren Seiten",
|
||||||
"with-comments": "Nur Anmerkungen mit Kommentaren"
|
"with-comments": "Nur Anmerkungen mit Kommentaren"
|
||||||
},
|
},
|
||||||
|
"filter": {
|
||||||
|
"analysis": "Analyse erforderlich",
|
||||||
|
"comment": "Kommentare",
|
||||||
|
"hint": "Nut Hinweise",
|
||||||
|
"image": "Bilder",
|
||||||
|
"none": "Keine Anmerkungen",
|
||||||
|
"redaction": "Geschwärzt",
|
||||||
|
"updated": "Aktualisiert"
|
||||||
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"assigned-people": "Beauftragt",
|
"assigned-people": "Beauftragt",
|
||||||
"documents-status": "Documents State",
|
"documents-status": "Documents State",
|
||||||
@ -1671,13 +1605,6 @@
|
|||||||
},
|
},
|
||||||
"title": "SMTP-Konto konfigurieren"
|
"title": "SMTP-Konto konfigurieren"
|
||||||
},
|
},
|
||||||
"generic-errors": {
|
|
||||||
"400": "The sent request is not valid.",
|
|
||||||
"403": "Access to the requested resource is not allowed.",
|
|
||||||
"404": "The requested resource could not be found.",
|
|
||||||
"409": "The request is incompatible with the current state.",
|
|
||||||
"500": "The server encountered an unexpected condition that prevented it from fulfilling the request."
|
|
||||||
},
|
|
||||||
"help-mode": {
|
"help-mode": {
|
||||||
"bottom-text": "Hilfe-Modus",
|
"bottom-text": "Hilfe-Modus",
|
||||||
"button-text": "Help Mode (H)",
|
"button-text": "Help Mode (H)",
|
||||||
@ -1789,6 +1716,7 @@
|
|||||||
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon",
|
"copyright-claim-text": "Copyright © 2020 - {currentYear} knecon",
|
||||||
"copyright-claim-title": "Copyright",
|
"copyright-claim-title": "Copyright",
|
||||||
"custom-app-title": "Name der Anwendung",
|
"custom-app-title": "Name der Anwendung",
|
||||||
|
"email-report": "E-Mail-Bericht",
|
||||||
"email": {
|
"email": {
|
||||||
"body": {
|
"body": {
|
||||||
"analyzed": "Im aktuellen Lizenzzeitraum insgesamt analysierte Seiten: {pages}.",
|
"analyzed": "Im aktuellen Lizenzzeitraum insgesamt analysierte Seiten: {pages}.",
|
||||||
@ -1796,7 +1724,6 @@
|
|||||||
},
|
},
|
||||||
"title": "Lizenzbericht {licenseCustomer}"
|
"title": "Lizenzbericht {licenseCustomer}"
|
||||||
},
|
},
|
||||||
"email-report": "E-Mail-Bericht",
|
|
||||||
"end-user-license-text": "Die Nutzung dieses Produkts unterliegt den Bedingungen der Endbenutzer-Lizenzvereinbarung für den RedactManager, sofern darin nichts anderweitig festgelegt.",
|
"end-user-license-text": "Die Nutzung dieses Produkts unterliegt den Bedingungen der Endbenutzer-Lizenzvereinbarung für den RedactManager, sofern darin nichts anderweitig festgelegt.",
|
||||||
"end-user-license-title": "Endbenutzer-Lizenzvereinbarung",
|
"end-user-license-title": "Endbenutzer-Lizenzvereinbarung",
|
||||||
"licensing-details": {
|
"licensing-details": {
|
||||||
@ -1889,13 +1816,6 @@
|
|||||||
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
|
"user-promoted-to-approver": "<b>{user}</b> wurde im Dossier <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> zum Genehmiger ernannt!",
|
||||||
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
|
"user-removed-as-dossier-member": "<b>{user}</b> wurde als Mitglied von: <b>{dossierHref, select, null{{dossierName}} other{<a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a>}}</b> entfernt!"
|
||||||
},
|
},
|
||||||
"notifications": {
|
|
||||||
"button-text": "Notifications",
|
|
||||||
"deleted-dossier": "Deleted Dossier",
|
|
||||||
"label": "Benachrichtigungen",
|
|
||||||
"mark-all-as-read": "Alle als gelesen markieren",
|
|
||||||
"mark-as": "Mark as {type, select, read{read} unread{unread} other{}}"
|
|
||||||
},
|
|
||||||
"notifications-screen": {
|
"notifications-screen": {
|
||||||
"category": {
|
"category": {
|
||||||
"email-notifications": "E-Mail Benachrichtigungen",
|
"email-notifications": "E-Mail Benachrichtigungen",
|
||||||
@ -1909,6 +1829,7 @@
|
|||||||
"dossier": "Dossierbezogene Benachrichtigungen",
|
"dossier": "Dossierbezogene Benachrichtigungen",
|
||||||
"other": "Andere Benachrichtigungen"
|
"other": "Andere Benachrichtigungen"
|
||||||
},
|
},
|
||||||
|
"options-title": "Wählen Sie aus, in welcher Kategorie Sie benachrichtigt werden möchten",
|
||||||
"options": {
|
"options": {
|
||||||
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin",
|
"ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin",
|
||||||
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Überprüfer zugewiesen bin",
|
"ASSIGN_REVIEWER": "Wenn ich einem Dokument als Überprüfer zugewiesen bin",
|
||||||
@ -1926,7 +1847,6 @@
|
|||||||
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
|
"USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde",
|
||||||
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
|
"USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere"
|
||||||
},
|
},
|
||||||
"options-title": "Wählen Sie aus, in welcher Kategorie Sie benachrichtigt werden möchten",
|
|
||||||
"schedule": {
|
"schedule": {
|
||||||
"daily": "Tägliche Zusammenfassung",
|
"daily": "Tägliche Zusammenfassung",
|
||||||
"instant": "Sofortig",
|
"instant": "Sofortig",
|
||||||
@ -1934,6 +1854,13 @@
|
|||||||
},
|
},
|
||||||
"title": "Benachrichtigungseinstellungen"
|
"title": "Benachrichtigungseinstellungen"
|
||||||
},
|
},
|
||||||
|
"notifications": {
|
||||||
|
"button-text": "Notifications",
|
||||||
|
"deleted-dossier": "Deleted Dossier",
|
||||||
|
"label": "Benachrichtigungen",
|
||||||
|
"mark-all-as-read": "Alle als gelesen markieren",
|
||||||
|
"mark-as": "Mark as {type, select, read{read} unread{unread} other{}}"
|
||||||
|
},
|
||||||
"ocr": {
|
"ocr": {
|
||||||
"confirmation-dialog": {
|
"confirmation-dialog": {
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
@ -2025,16 +1952,16 @@
|
|||||||
"warnings-subtitle": "Do not show again options",
|
"warnings-subtitle": "Do not show again options",
|
||||||
"warnings-title": "Prompts and Dialogs Settings"
|
"warnings-title": "Prompts and Dialogs Settings"
|
||||||
},
|
},
|
||||||
"processing": {
|
|
||||||
"basic": "Processing",
|
|
||||||
"ocr": "OCR"
|
|
||||||
},
|
|
||||||
"processing-status": {
|
"processing-status": {
|
||||||
"ocr": "OCR",
|
"ocr": "OCR",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
"processed": "Processed",
|
"processed": "Processed",
|
||||||
"processing": "Processing"
|
"processing": "Processing"
|
||||||
},
|
},
|
||||||
|
"processing": {
|
||||||
|
"basic": "Processing",
|
||||||
|
"ocr": "OCR"
|
||||||
|
},
|
||||||
"readonly": "Lesemodus",
|
"readonly": "Lesemodus",
|
||||||
"readonly-archived": "Read only (archived)",
|
"readonly-archived": "Read only (archived)",
|
||||||
"redact-text": {
|
"redact-text": {
|
||||||
@ -2264,12 +2191,6 @@
|
|||||||
"red-user-admin": "Benutzer-Admin",
|
"red-user-admin": "Benutzer-Admin",
|
||||||
"regular": "Regulär"
|
"regular": "Regulär"
|
||||||
},
|
},
|
||||||
"search": {
|
|
||||||
"active-dossiers": "ganze Plattform",
|
|
||||||
"all-dossiers": "all documents",
|
|
||||||
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
|
|
||||||
"this-dossier": "in diesem Dossier"
|
|
||||||
},
|
|
||||||
"search-screen": {
|
"search-screen": {
|
||||||
"cols": {
|
"cols": {
|
||||||
"assignee": "Bevollmächtigter",
|
"assignee": "Bevollmächtigter",
|
||||||
@ -2293,6 +2214,12 @@
|
|||||||
"no-match": "Keine Dokumente entsprechen Ihren aktuellen Filtern.",
|
"no-match": "Keine Dokumente entsprechen Ihren aktuellen Filtern.",
|
||||||
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
|
"table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}"
|
||||||
},
|
},
|
||||||
|
"search": {
|
||||||
|
"active-dossiers": "ganze Plattform",
|
||||||
|
"all-dossiers": "all documents",
|
||||||
|
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
|
||||||
|
"this-dossier": "in diesem Dossier"
|
||||||
|
},
|
||||||
"seconds": "seconds",
|
"seconds": "seconds",
|
||||||
"size": "Size",
|
"size": "Size",
|
||||||
"smtp-auth-config": {
|
"smtp-auth-config": {
|
||||||
@ -2379,16 +2306,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"unapproved-suggestions": {
|
|
||||||
"confirmation-dialog": {
|
|
||||||
"checkbox-text": "Do not show this message again",
|
|
||||||
"confirmation-text": "Continue to Preview",
|
|
||||||
"deny-text": "Cancel",
|
|
||||||
"not-displayed-question": "The document contains unapproved suggestions that are not included in the preview.",
|
|
||||||
"success-confirmation-text": "Prompts about unapproved suggestions were disabled. Change this preference in <a href={settingsUrl} target=_self class=\"toast-link\">account settings</a>.",
|
|
||||||
"title": "Document with unapproved suggestions"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unknown": "Unbekannt",
|
"unknown": "Unbekannt",
|
||||||
"update-profile": {
|
"update-profile": {
|
||||||
"errors": {
|
"errors": {
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"add-to-dictionary": "Add to dictionary",
|
"add-to-dictionary": "Add to dictionary",
|
||||||
"request-add-to-dictionary": "Request add to dictionary"
|
"request-add-to-dictionary": "Request add to dictionary"
|
||||||
},
|
}
|
||||||
"multiple-values": "Multiple recommendations selected"
|
|
||||||
},
|
},
|
||||||
"account-settings": "Account Settings",
|
"account-settings": "Account Settings",
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -56,8 +55,7 @@
|
|||||||
},
|
},
|
||||||
"add-edit-clone-dossier-template": {
|
"add-edit-clone-dossier-template": {
|
||||||
"error": {
|
"error": {
|
||||||
"conflict": "Failed to create dossier template: a dossier template with the same name already exists.",
|
"conflict": "Failed to create dossier template: a dossier template with the same name already exists."
|
||||||
"generic": "Failed to create dossier template."
|
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"apply-updates-default": {
|
"apply-updates-default": {
|
||||||
@ -280,26 +278,10 @@
|
|||||||
"error": "Failed to add entry to dictionary: {error}",
|
"error": "Failed to add entry to dictionary: {error}",
|
||||||
"success": "Entry added to dictionary. Changes will be visible after reanalysis."
|
"success": "Entry added to dictionary. Changes will be visible after reanalysis."
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Failed to approve dictionary entry: {error}",
|
|
||||||
"success": "Dictionary entry approved. Changes will be visible after reanalysis."
|
|
||||||
},
|
|
||||||
"decline": {
|
|
||||||
"error": "Failed to decline dictionary suggestion: {error}",
|
|
||||||
"success": "Dictionary suggestion declined."
|
|
||||||
},
|
|
||||||
"remove": {
|
"remove": {
|
||||||
"error": "Failed to remove dictionary entry: {error}",
|
"error": "Failed to remove dictionary entry: {error}",
|
||||||
"success": "Dictionary entry removed!"
|
"success": "Dictionary entry removed!"
|
||||||
},
|
},
|
||||||
"request-remove": {
|
|
||||||
"error": "Failed to request removal of dictionary entry: {error}",
|
|
||||||
"success": "Requested to remove dictionary entry!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Failed to save suggestion for dictionary modification: {error}",
|
|
||||||
"success": "Suggestion for dictionary modification saved!"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -310,10 +292,6 @@
|
|||||||
"error": "Failed to save redaction: {error}",
|
"error": "Failed to save redaction: {error}",
|
||||||
"success": "Redaction added!"
|
"success": "Redaction added!"
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Failed to approve suggestion: {error}",
|
|
||||||
"success": "Suggestion approved."
|
|
||||||
},
|
|
||||||
"change-legal-basis": {
|
"change-legal-basis": {
|
||||||
"error": "Failed to edit annotation reason: {error}",
|
"error": "Failed to edit annotation reason: {error}",
|
||||||
"success": "Annotation reason was edited."
|
"success": "Annotation reason was edited."
|
||||||
@ -322,10 +300,6 @@
|
|||||||
"error": "Failed to edit type: {error}",
|
"error": "Failed to edit type: {error}",
|
||||||
"success": "Type was edited."
|
"success": "Type was edited."
|
||||||
},
|
},
|
||||||
"decline": {
|
|
||||||
"error": "Failed to decline suggestion: {error}",
|
|
||||||
"success": "Suggestion declined."
|
|
||||||
},
|
|
||||||
"force-hint": {
|
"force-hint": {
|
||||||
"error": "Failed to save hint: {error}",
|
"error": "Failed to save hint: {error}",
|
||||||
"success": "Hint added!"
|
"success": "Hint added!"
|
||||||
@ -346,34 +320,6 @@
|
|||||||
"error": "Failed to remove redaction: {error}",
|
"error": "Failed to remove redaction: {error}",
|
||||||
"success": "Redaction removed!"
|
"success": "Redaction removed!"
|
||||||
},
|
},
|
||||||
"request-change-legal-basis": {
|
|
||||||
"error": "Failed to request annotation reason change: {error}",
|
|
||||||
"success": "Annotation reason change requested."
|
|
||||||
},
|
|
||||||
"request-force-hint": {
|
|
||||||
"error": "Failed to save hint suggestion: {error}",
|
|
||||||
"success": "Hint suggestion saved"
|
|
||||||
},
|
|
||||||
"request-force-redaction": {
|
|
||||||
"error": "Failed to save redaction suggestion: {error}",
|
|
||||||
"success": "Redaction suggestion saved"
|
|
||||||
},
|
|
||||||
"request-image-recategorization": {
|
|
||||||
"error": "Failed to request image recategorization: {error}",
|
|
||||||
"success": "Image recategorization requested."
|
|
||||||
},
|
|
||||||
"request-remove-hint": {
|
|
||||||
"error": "Failed to request removal of hint: {error}",
|
|
||||||
"success": "Requested to remove hint!"
|
|
||||||
},
|
|
||||||
"request-remove": {
|
|
||||||
"error": "Failed to request removal of redaction: {error}",
|
|
||||||
"success": "Requested to remove redaction!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Failed to save redaction suggestion: {error}",
|
|
||||||
"success": "Redaction suggestion saved"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -416,7 +362,6 @@
|
|||||||
"rule": "Redaction based on rule {rule}"
|
"rule": "Redaction based on rule {rule}"
|
||||||
},
|
},
|
||||||
"annotation-type": {
|
"annotation-type": {
|
||||||
"declined-suggestion": "Declined Suggestion",
|
|
||||||
"hint": "Hint",
|
"hint": "Hint",
|
||||||
"ignored-hint": "Ignored Hint",
|
"ignored-hint": "Ignored Hint",
|
||||||
"manual-hint": "Manual Hint",
|
"manual-hint": "Manual Hint",
|
||||||
@ -424,16 +369,6 @@
|
|||||||
"recommendation": "Recommendation",
|
"recommendation": "Recommendation",
|
||||||
"redaction": "Redaction",
|
"redaction": "Redaction",
|
||||||
"skipped": "Skipped",
|
"skipped": "Skipped",
|
||||||
"suggestion-add": "Suggested redaction",
|
|
||||||
"suggestion-add-dictionary": "Suggested add to Dictionary",
|
|
||||||
"suggestion-add-false-positive": "Suggested add to false positive",
|
|
||||||
"suggestion-change-legal-basis": "Suggested change legal basis",
|
|
||||||
"suggestion-force-hint": "Suggestion force hint",
|
|
||||||
"suggestion-force-redaction": "Suggestion force redaction",
|
|
||||||
"suggestion-recategorize-image": "Suggested recategorize image",
|
|
||||||
"suggestion-remove": "Suggested local removal",
|
|
||||||
"suggestion-remove-dictionary": "Suggested dictionary removal",
|
|
||||||
"suggestion-resize": "Suggested Resize",
|
|
||||||
"text-highlight": "Earmark"
|
"text-highlight": "Earmark"
|
||||||
},
|
},
|
||||||
"annotation": {
|
"annotation": {
|
||||||
@ -1601,7 +1536,6 @@
|
|||||||
"image": "Images",
|
"image": "Images",
|
||||||
"none": "No Annotations",
|
"none": "No Annotations",
|
||||||
"redaction": "Redacted",
|
"redaction": "Redacted",
|
||||||
"suggestion": "Suggested Redaction",
|
|
||||||
"updated": "Updated"
|
"updated": "Updated"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
@ -2372,16 +2306,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unapproved-suggestions": {
|
|
||||||
"confirmation-dialog": {
|
|
||||||
"checkbox-text": "Do not show this message again",
|
|
||||||
"confirmation-text": "Continue to Preview",
|
|
||||||
"deny-text": "Cancel",
|
|
||||||
"not-displayed-question": "The document contains unapproved suggestions that are not included in the preview.",
|
|
||||||
"success-confirmation-text": "Prompts about unapproved suggestions were disabled. Change this preference in <a href={settingsUrl} target=_self class=\"toast-link\">account settings</a>.",
|
|
||||||
"title": "Document with unapproved suggestions"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
"update-profile": {
|
"update-profile": {
|
||||||
"errors": {
|
"errors": {
|
||||||
@ -2545,12 +2469,5 @@
|
|||||||
"select": "Select"
|
"select": "Select"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"yesterday": "Yesterday",
|
"yesterday": "Yesterday"
|
||||||
"generic-errors": {
|
|
||||||
"400": "The sent request is not valid.",
|
|
||||||
"403": "Access to the requested resource is not allowed.",
|
|
||||||
"404": "The requested resource could not be found.",
|
|
||||||
"409": "The request is incompatible with the current state.",
|
|
||||||
"500": "The server encountered an unexpected condition that prevented it from fulfilling the request."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"add-to-dictionary": "",
|
"add-to-dictionary": "",
|
||||||
"request-add-to-dictionary": ""
|
"request-add-to-dictionary": ""
|
||||||
},
|
}
|
||||||
"multiple-values": ""
|
|
||||||
},
|
},
|
||||||
"account-settings": "Account Einstellungen",
|
"account-settings": "Account Einstellungen",
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -56,8 +55,7 @@
|
|||||||
},
|
},
|
||||||
"add-edit-clone-dossier-template": {
|
"add-edit-clone-dossier-template": {
|
||||||
"error": {
|
"error": {
|
||||||
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen.",
|
"conflict": "Dossiervorlage konnte nicht erstellt werden: Es existiert bereits eine Dossiervorlage mit demselben Namen."
|
||||||
"generic": "Fehler beim Erstellen der Dossiervorlage."
|
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"apply-updates-default": {
|
"apply-updates-default": {
|
||||||
@ -280,26 +278,10 @@
|
|||||||
"error": "Fehler beim Hinzufügen des neuen Wörterbucheintrags: {error}",
|
"error": "Fehler beim Hinzufügen des neuen Wörterbucheintrags: {error}",
|
||||||
"success": "Eintrag zum Wörterbuch hinzugefügt. Änderungen nach der Reanalyse sichtbar."
|
"success": "Eintrag zum Wörterbuch hinzugefügt. Änderungen nach der Reanalyse sichtbar."
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Fehler beim Genehmigen des Wörterbucheintrags: {error}",
|
|
||||||
"success": "Neuer Wörterbucheintrag wurde genehmigt. Änderungen sind nach der Reanalyse sichtbar."
|
|
||||||
},
|
|
||||||
"decline": {
|
|
||||||
"error": "Fehler beim Ablehnen des neuen Wörterbucheintrags: {error}",
|
|
||||||
"success": "Vorschlag für das Wörterbuch abgelehnt."
|
|
||||||
},
|
|
||||||
"remove": {
|
"remove": {
|
||||||
"error": "Fehler beim Entfernen des Wörterbucheintrags: {error}",
|
"error": "Fehler beim Entfernen des Wörterbucheintrags: {error}",
|
||||||
"success": "Wörterbucheintrag wurde gelöscht!"
|
"success": "Wörterbucheintrag wurde gelöscht!"
|
||||||
},
|
},
|
||||||
"request-remove": {
|
|
||||||
"error": "Dossier-Vorlage",
|
|
||||||
"success": "Löschung des Wörterbucheintrags wurde vorgeschlagen!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Vorschlag für Änderung des Wörterbuchs konnte nicht gespeichert werden: {error}",
|
|
||||||
"success": "Vorschlag für die Änderung des Wörterbuchs gespeichert!"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
"success": "Rückgängigmachen erfolgreich"
|
"success": "Rückgängigmachen erfolgreich"
|
||||||
@ -310,10 +292,6 @@
|
|||||||
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
"error": "Fehler beim Speichern der Schwärzung: {error}",
|
||||||
"success": "Schwärzung hinzugefügt!"
|
"success": "Schwärzung hinzugefügt!"
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Fehler beim Genehmigen des Vorschlags: {error}",
|
|
||||||
"success": "Vorschlag genehmigt"
|
|
||||||
},
|
|
||||||
"change-legal-basis": {
|
"change-legal-basis": {
|
||||||
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
"error": "Fehler beim Bearbeiten der in der Anmerkung genannten Begründung: {error}",
|
||||||
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
"success": "In der Anmerkung genannte Begründung wurde bearbeitet."
|
||||||
@ -322,10 +300,6 @@
|
|||||||
"error": "",
|
"error": "",
|
||||||
"success": ""
|
"success": ""
|
||||||
},
|
},
|
||||||
"decline": {
|
|
||||||
"error": "Fehler beim Ablehnen des Vorschlags: {error}",
|
|
||||||
"success": "Vorschlag abgelehnt"
|
|
||||||
},
|
|
||||||
"force-hint": {
|
"force-hint": {
|
||||||
"error": "",
|
"error": "",
|
||||||
"success": ""
|
"success": ""
|
||||||
@ -346,34 +320,6 @@
|
|||||||
"error": "Fehler beim Entfernen der Schwärzung: {error}",
|
"error": "Fehler beim Entfernen der Schwärzung: {error}",
|
||||||
"success": "Schwärzung entfernt!"
|
"success": "Schwärzung entfernt!"
|
||||||
},
|
},
|
||||||
"request-change-legal-basis": {
|
|
||||||
"error": "Fehler beim Vorschlagen der Änderung der Begründung:",
|
|
||||||
"success": "Die Änderung der in der Anmerkung genannten Begründung wurde beantragt."
|
|
||||||
},
|
|
||||||
"request-force-hint": {
|
|
||||||
"error": "",
|
|
||||||
"success": ""
|
|
||||||
},
|
|
||||||
"request-force-redaction": {
|
|
||||||
"error": "Fehler beim Speichern des Schwärzungsvorschlags: {error}",
|
|
||||||
"success": "Vorschlag einer Schwärzung gespeichert"
|
|
||||||
},
|
|
||||||
"request-image-recategorization": {
|
|
||||||
"error": "Fehler beim Vorschlagen der Neukategorisierung des Bilds: {error}",
|
|
||||||
"success": "Bild-Neuklassifizierung angefordert."
|
|
||||||
},
|
|
||||||
"request-remove-hint": {
|
|
||||||
"error": "",
|
|
||||||
"success": ""
|
|
||||||
},
|
|
||||||
"request-remove": {
|
|
||||||
"error": "Fehler beim Erstellen des Vorschlags für das Entfernen der Schwärzung: {error}",
|
|
||||||
"success": "Entfernen der Schwärzung wurde vorgeschlagen!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Vorschlag einer Schwärzung wurde nicht gespeichert: {error}",
|
|
||||||
"success": "Vorschlag einer Schwärzung gespeichert"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
"error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}",
|
||||||
"success": "erfolgreich Rückgängig gemacht"
|
"success": "erfolgreich Rückgängig gemacht"
|
||||||
@ -416,7 +362,6 @@
|
|||||||
"rule": "Schwärzung basierend auf Regel {rule}"
|
"rule": "Schwärzung basierend auf Regel {rule}"
|
||||||
},
|
},
|
||||||
"annotation-type": {
|
"annotation-type": {
|
||||||
"declined-suggestion": "Abgelehnter Vorschlag",
|
|
||||||
"hint": "Hinweis",
|
"hint": "Hinweis",
|
||||||
"ignored-hint": "Ignorierter Hinweis",
|
"ignored-hint": "Ignorierter Hinweis",
|
||||||
"manual-hint": "",
|
"manual-hint": "",
|
||||||
@ -424,16 +369,6 @@
|
|||||||
"recommendation": "Empfehlung",
|
"recommendation": "Empfehlung",
|
||||||
"redaction": "Schwärzung",
|
"redaction": "Schwärzung",
|
||||||
"skipped": "Übersprungen",
|
"skipped": "Übersprungen",
|
||||||
"suggestion-add": "Vorschlag für Schwärzung",
|
|
||||||
"suggestion-add-dictionary": "Vorschlag für neuen Wörterbucheintrag",
|
|
||||||
"suggestion-add-false-positive": "",
|
|
||||||
"suggestion-change-legal-basis": "Vorschlag für Änderung der Rechtsgrundlage",
|
|
||||||
"suggestion-force-hint": "",
|
|
||||||
"suggestion-force-redaction": "Vorschlag für erzwungene Schwärzung",
|
|
||||||
"suggestion-recategorize-image": "Vorschlag für Rekategorisierung eines Bilds",
|
|
||||||
"suggestion-remove": "Vorschlagen, die Schwärzung zu entfernen",
|
|
||||||
"suggestion-remove-dictionary": "Vorschlag für Löschung eines Wörterbucheintrags",
|
|
||||||
"suggestion-resize": "Vorgeschlagene Größenänderung",
|
|
||||||
"text-highlight": ""
|
"text-highlight": ""
|
||||||
},
|
},
|
||||||
"annotation": {
|
"annotation": {
|
||||||
@ -1601,7 +1536,6 @@
|
|||||||
"image": "Bilder",
|
"image": "Bilder",
|
||||||
"none": "Keine Anmerkungen",
|
"none": "Keine Anmerkungen",
|
||||||
"redaction": "Geschwärzt",
|
"redaction": "Geschwärzt",
|
||||||
"suggestion": "Vorgeschlagene Schwärzung",
|
|
||||||
"updated": "Aktualisiert"
|
"updated": "Aktualisiert"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
@ -2372,16 +2306,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"unapproved-suggestions": {
|
|
||||||
"confirmation-dialog": {
|
|
||||||
"checkbox-text": "",
|
|
||||||
"confirmation-text": "",
|
|
||||||
"deny-text": "",
|
|
||||||
"not-displayed-question": "",
|
|
||||||
"success-confirmation-text": "",
|
|
||||||
"title": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unknown": "Unbekannt",
|
"unknown": "Unbekannt",
|
||||||
"update-profile": {
|
"update-profile": {
|
||||||
"errors": {
|
"errors": {
|
||||||
@ -2545,12 +2469,5 @@
|
|||||||
"select": "Wählen"
|
"select": "Wählen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"yesterday": "Gestern",
|
"yesterday": "Gestern"
|
||||||
"generic-errors": {
|
|
||||||
"400": "",
|
|
||||||
"403": "",
|
|
||||||
"404": "",
|
|
||||||
"409": "",
|
|
||||||
"500": ""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"add-to-dictionary": "Add to dictionary",
|
"add-to-dictionary": "Add to dictionary",
|
||||||
"request-add-to-dictionary": "Request add to dictionary"
|
"request-add-to-dictionary": "Request add to dictionary"
|
||||||
},
|
}
|
||||||
"multiple-values": "Multiple recommendations selected"
|
|
||||||
},
|
},
|
||||||
"account-settings": "Account Settings",
|
"account-settings": "Account Settings",
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -56,8 +55,7 @@
|
|||||||
},
|
},
|
||||||
"add-edit-clone-dossier-template": {
|
"add-edit-clone-dossier-template": {
|
||||||
"error": {
|
"error": {
|
||||||
"conflict": "Failed to create dossier template: a dossier template with the same name already exists.",
|
"conflict": "Failed to create dossier template: a dossier template with the same name already exists."
|
||||||
"generic": "Failed to create dossier template."
|
|
||||||
},
|
},
|
||||||
"form": {
|
"form": {
|
||||||
"apply-updates-default": {
|
"apply-updates-default": {
|
||||||
@ -280,26 +278,10 @@
|
|||||||
"error": "Failed to add entry to dictionary: {error}",
|
"error": "Failed to add entry to dictionary: {error}",
|
||||||
"success": "Entry added to dictionary. Changes will be visible after reanalysis."
|
"success": "Entry added to dictionary. Changes will be visible after reanalysis."
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Failed to approve dictionary entry: {error}",
|
|
||||||
"success": "Dictionary entry approved. Changes will be visible after reanalysis."
|
|
||||||
},
|
|
||||||
"decline": {
|
|
||||||
"error": "Failed to decline dictionary suggestion: {error}",
|
|
||||||
"success": "Dictionary suggestion declined."
|
|
||||||
},
|
|
||||||
"remove": {
|
"remove": {
|
||||||
"error": "Failed to remove dictionary entry: {error}",
|
"error": "Failed to remove dictionary entry: {error}",
|
||||||
"success": "Dictionary entry removed!"
|
"success": "Dictionary entry removed!"
|
||||||
},
|
},
|
||||||
"request-remove": {
|
|
||||||
"error": "Failed to request removal of dictionary entry: {error}",
|
|
||||||
"success": "Requested to remove dictionary entry!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Failed to save suggestion for dictionary modification: {error}",
|
|
||||||
"success": "Suggestion for dictionary modification saved!"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -310,10 +292,6 @@
|
|||||||
"error": "Failed to save annotation: {error}",
|
"error": "Failed to save annotation: {error}",
|
||||||
"success": "Annotation added!"
|
"success": "Annotation added!"
|
||||||
},
|
},
|
||||||
"approve": {
|
|
||||||
"error": "Failed to approve suggestion: {error}",
|
|
||||||
"success": "Suggestion approved."
|
|
||||||
},
|
|
||||||
"change-legal-basis": {
|
"change-legal-basis": {
|
||||||
"error": "Failed to edit annotation reason: {error}",
|
"error": "Failed to edit annotation reason: {error}",
|
||||||
"success": "Annotation reason was edited."
|
"success": "Annotation reason was edited."
|
||||||
@ -322,10 +300,6 @@
|
|||||||
"error": "",
|
"error": "",
|
||||||
"success": ""
|
"success": ""
|
||||||
},
|
},
|
||||||
"decline": {
|
|
||||||
"error": "Failed to decline suggestion: {error}",
|
|
||||||
"success": "Suggestion declined."
|
|
||||||
},
|
|
||||||
"force-hint": {
|
"force-hint": {
|
||||||
"error": "Failed to save hint: {error}",
|
"error": "Failed to save hint: {error}",
|
||||||
"success": "Hint added!"
|
"success": "Hint added!"
|
||||||
@ -346,34 +320,6 @@
|
|||||||
"error": "Failed to remove annotation: {error}",
|
"error": "Failed to remove annotation: {error}",
|
||||||
"success": "Annotation removed!"
|
"success": "Annotation removed!"
|
||||||
},
|
},
|
||||||
"request-change-legal-basis": {
|
|
||||||
"error": "Failed to request annotation reason change: {error}",
|
|
||||||
"success": "Annotation reason change requested."
|
|
||||||
},
|
|
||||||
"request-force-hint": {
|
|
||||||
"error": "Failed to save hint suggestion: {error}",
|
|
||||||
"success": "Hint suggestion saved"
|
|
||||||
},
|
|
||||||
"request-force-redaction": {
|
|
||||||
"error": "Failed to save annotation suggestion: {error}",
|
|
||||||
"success": "Annotation suggestion saved"
|
|
||||||
},
|
|
||||||
"request-image-recategorization": {
|
|
||||||
"error": "Failed to request image recategorization: {error}",
|
|
||||||
"success": "Image recategorization requested."
|
|
||||||
},
|
|
||||||
"request-remove-hint": {
|
|
||||||
"error": "Failed to request removal of hint: {error}",
|
|
||||||
"success": "Requested to remove hint!"
|
|
||||||
},
|
|
||||||
"request-remove": {
|
|
||||||
"error": "Failed to request removal of annotation: {error}",
|
|
||||||
"success": "Requested to remove annotation!"
|
|
||||||
},
|
|
||||||
"suggest": {
|
|
||||||
"error": "Failed to save annotation suggestion: {error}",
|
|
||||||
"success": "Annotation suggestion saved"
|
|
||||||
},
|
|
||||||
"undo": {
|
"undo": {
|
||||||
"error": "Failed to undo: {error}",
|
"error": "Failed to undo: {error}",
|
||||||
"success": "Undo successful"
|
"success": "Undo successful"
|
||||||
@ -416,7 +362,6 @@
|
|||||||
"rule": "Annotation based on rule {rule}"
|
"rule": "Annotation based on rule {rule}"
|
||||||
},
|
},
|
||||||
"annotation-type": {
|
"annotation-type": {
|
||||||
"declined-suggestion": "Declined Suggestion",
|
|
||||||
"hint": "Hint",
|
"hint": "Hint",
|
||||||
"ignored-hint": "Ignored Hint",
|
"ignored-hint": "Ignored Hint",
|
||||||
"manual-hint": "Manual Hint",
|
"manual-hint": "Manual Hint",
|
||||||
@ -424,16 +369,6 @@
|
|||||||
"recommendation": "Recommendation",
|
"recommendation": "Recommendation",
|
||||||
"redaction": "Annotation",
|
"redaction": "Annotation",
|
||||||
"skipped": "Skipped",
|
"skipped": "Skipped",
|
||||||
"suggestion-add": "Suggested annotation",
|
|
||||||
"suggestion-add-dictionary": "Suggested add to Dictionary",
|
|
||||||
"suggestion-add-false-positive": "Suggested add to false positive",
|
|
||||||
"suggestion-change-legal-basis": "Suggested change legal basis",
|
|
||||||
"suggestion-force-hint": "Suggestion force hint",
|
|
||||||
"suggestion-force-redaction": "Suggestion force Annotation",
|
|
||||||
"suggestion-recategorize-image": "Suggested recategorize image",
|
|
||||||
"suggestion-remove": "Suggested local removal",
|
|
||||||
"suggestion-remove-dictionary": "Suggested dictionary removal",
|
|
||||||
"suggestion-resize": "Suggested Resize",
|
|
||||||
"text-highlight": "Earmark"
|
"text-highlight": "Earmark"
|
||||||
},
|
},
|
||||||
"annotation": {
|
"annotation": {
|
||||||
@ -1508,7 +1443,7 @@
|
|||||||
"redacted": "Preview",
|
"redacted": "Preview",
|
||||||
"redacted-tooltip": "Component preview shows only annotations. Consider this a preview for the final version. This view is only available if the file has no pending changes & doesn't require a reanalysis",
|
"redacted-tooltip": "Component preview shows only annotations. Consider this a preview for the final version. This view is only available if the file has no pending changes & doesn't require a reanalysis",
|
||||||
"standard": "Standard",
|
"standard": "Standard",
|
||||||
"standard-tooltip": "Standard Workload view shows all hints, annotations, recommendations & suggestions. This view allows editing.",
|
"standard-tooltip": "Standard Workload view shows all hints, annotations & recommendations. This view allows editing.",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"jump-to-next": "Jump to Next",
|
"jump-to-next": "Jump to Next",
|
||||||
@ -1601,7 +1536,6 @@
|
|||||||
"image": "Images",
|
"image": "Images",
|
||||||
"none": "No Annotations",
|
"none": "No Annotations",
|
||||||
"redaction": "Annotations",
|
"redaction": "Annotations",
|
||||||
"suggestion": "Suggested Annotation",
|
|
||||||
"updated": "Updated"
|
"updated": "Updated"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
@ -2372,16 +2306,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unapproved-suggestions": {
|
|
||||||
"confirmation-dialog": {
|
|
||||||
"checkbox-text": "Do not show this message again",
|
|
||||||
"confirmation-text": "Continue to Preview",
|
|
||||||
"deny-text": "Cancel",
|
|
||||||
"not-displayed-question": "The document contains unapproved suggestions that are not included in the preview.",
|
|
||||||
"success-confirmation-text": "Prompts about unapproved suggestions were disabled. Change this preference in <a href={settingsUrl} target=_self class=\"toast-link\">account settings</a>.",
|
|
||||||
"title": "Document with unapproved suggestions"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"unknown": "Unknown",
|
"unknown": "Unknown",
|
||||||
"update-profile": {
|
"update-profile": {
|
||||||
"errors": {
|
"errors": {
|
||||||
@ -2545,12 +2469,5 @@
|
|||||||
"select": "Select"
|
"select": "Select"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"yesterday": "Yesterday",
|
"yesterday": "Yesterday"
|
||||||
"generic-errors": {
|
|
||||||
"400": "The sent request is not valid.",
|
|
||||||
"403": "Access to the requested resource is not allowed.",
|
|
||||||
"404": "The requested resource could not be found.",
|
|
||||||
"409": "The request is incompatible with the current state.",
|
|
||||||
"500": "The server encountered an unexpected condition that prevented it from fulfilling the request."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
FROM node:20.8-buster as builder
|
FROM node:20.9-buster as builder
|
||||||
|
|
||||||
WORKDIR /ng-app
|
WORKDIR /ng-app
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ CMD ["/bin/cp", "-r", "/ng-app/dist/paligo-styles", "/tmp/styles-export"]
|
|||||||
### STAGE 2: Setup ###
|
### STAGE 2: Setup ###
|
||||||
|
|
||||||
|
|
||||||
FROM nginx:1.25.2-alpine
|
FROM nginx:1.25.3-alpine
|
||||||
|
|
||||||
## Copy our default nginx config
|
## Copy our default nginx config
|
||||||
COPY docker/common/nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY docker/common/nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit a6383c1dbc840115897a31567c3f5633ba78b43a
|
Subproject commit 85fba4a1dd57ecbb133d938552d75fceffa1099c
|
||||||
@ -1,25 +1,16 @@
|
|||||||
export type ImageCategory = 'signature' | 'logo' | 'formula' | 'image';
|
export type ImageCategory = 'signature' | 'logo' | 'formula' | 'image';
|
||||||
|
|
||||||
export type DictionaryActions = 'add' | 'approve' | 'remove' | 'decline' | 'request-remove' | 'suggest' | 'undo';
|
export type DictionaryActions = 'add' | 'remove' | 'undo';
|
||||||
|
|
||||||
export type ManualRedactionActions =
|
export type ManualRedactionActions =
|
||||||
| 'add'
|
| 'add'
|
||||||
| 'approve'
|
|
||||||
| 'remove'
|
| 'remove'
|
||||||
| 'remove-hint'
|
| 'remove-hint'
|
||||||
| 'change-legal-basis'
|
| 'change-legal-basis'
|
||||||
| 'decline'
|
|
||||||
| 'request-remove'
|
|
||||||
| 'request-remove-hint'
|
|
||||||
| 'request-change-legal-basis'
|
|
||||||
| 'recategorize-image'
|
| 'recategorize-image'
|
||||||
| 'request-image-recategorization'
|
|
||||||
| 'suggest'
|
|
||||||
| 'undo'
|
| 'undo'
|
||||||
| 'force-redaction'
|
| 'force-redaction'
|
||||||
| 'force-hint'
|
| 'force-hint'
|
||||||
| 'request-force-redaction'
|
|
||||||
| 'request-force-hint'
|
|
||||||
| 'change-type';
|
| 'change-type';
|
||||||
|
|
||||||
export const AnnotationIconTypes = {
|
export const AnnotationIconTypes = {
|
||||||
@ -36,7 +27,6 @@ export const AnnotationShapeMap: Record<string, AnnotationIconType> = {
|
|||||||
analysis: AnnotationIconTypes.square,
|
analysis: AnnotationIconTypes.square,
|
||||||
hint: AnnotationIconTypes.circle,
|
hint: AnnotationIconTypes.circle,
|
||||||
redaction: AnnotationIconTypes.square,
|
redaction: AnnotationIconTypes.square,
|
||||||
suggestion: AnnotationIconTypes.rhombus,
|
|
||||||
updated: AnnotationIconTypes.square,
|
updated: AnnotationIconTypes.square,
|
||||||
image: AnnotationIconTypes.square,
|
image: AnnotationIconTypes.square,
|
||||||
none: AnnotationIconTypes.none,
|
none: AnnotationIconTypes.none,
|
||||||
|
|||||||
@ -1,47 +1,30 @@
|
|||||||
import { DefaultColorType } from './default-color-type';
|
|
||||||
import { SuperType, SuperTypes } from '../files';
|
import { SuperType, SuperTypes } from '../files';
|
||||||
|
import { DefaultColorType } from './default-color-type';
|
||||||
|
|
||||||
export type DefaultBasedColorType = SuperType | 'updated' | 'suggestion' | 'analysis';
|
export type DefaultBasedColorType = Exclude<SuperType, typeof SuperTypes.TextHighlight> | 'updated' | 'analysis';
|
||||||
|
|
||||||
export const annotationDefaultColorConfig: Record<DefaultBasedColorType, DefaultColorType> = {
|
export const annotationDefaultColorConfig: Record<DefaultBasedColorType, DefaultColorType> = {
|
||||||
[SuperTypes.TextHighlight]: 'redactionColor', // not actually used
|
|
||||||
|
|
||||||
[SuperTypes.SuggestionChangeLegalBasis]: 'requestAddColor',
|
|
||||||
[SuperTypes.SuggestionRecategorizeImage]: 'requestAddColor',
|
|
||||||
[SuperTypes.SuggestionAddDictionary]: 'dictionaryRequestColor',
|
|
||||||
[SuperTypes.SuggestionForceRedaction]: 'requestAddColor',
|
|
||||||
[SuperTypes.SuggestionForceHint]: 'requestAddColor',
|
|
||||||
[SuperTypes.SuggestionResize]: 'dictionaryRequestColor',
|
|
||||||
[SuperTypes.SuggestionRemoveDictionary]: 'dictionaryRequestColor',
|
|
||||||
[SuperTypes.SuggestionAdd]: 'requestAddColor',
|
|
||||||
[SuperTypes.SuggestionRemove]: 'requestRemoveColor',
|
|
||||||
[SuperTypes.DeclinedSuggestion]: 'skippedColor',
|
|
||||||
|
|
||||||
[SuperTypes.IgnoredHint]: 'ignoredHintColor',
|
[SuperTypes.IgnoredHint]: 'ignoredHintColor',
|
||||||
[SuperTypes.Skipped]: 'skippedColor',
|
[SuperTypes.Skipped]: 'skippedColor',
|
||||||
[SuperTypes.Redaction]: 'redactionColor',
|
[SuperTypes.Redaction]: 'redactionColor',
|
||||||
[SuperTypes.ManualRedaction]: 'redactionColor',
|
[SuperTypes.ManualRedaction]: 'redactionColor',
|
||||||
|
[SuperTypes.ManualHint]: 'redactionColor',
|
||||||
[SuperTypes.Recommendation]: 'recommendationColor',
|
[SuperTypes.Recommendation]: 'recommendationColor',
|
||||||
[SuperTypes.Hint]: 'hintColor',
|
[SuperTypes.Hint]: 'hintColor',
|
||||||
|
|
||||||
updated: 'updatedColor',
|
updated: 'updatedColor',
|
||||||
suggestion: 'requestAddColor',
|
|
||||||
analysis: 'analysisColor',
|
analysis: 'analysisColor',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
type EntityBasedColorType =
|
export const annotationEntityColorConfig: Record<
|
||||||
| typeof SuperTypes.IgnoredHint
|
Exclude<SuperType, typeof SuperTypes.TextHighlight>,
|
||||||
| typeof SuperTypes.Skipped
|
'hexColor' | 'recommendationHexColor' | 'skippedHexColor'
|
||||||
| typeof SuperTypes.Redaction
|
> = {
|
||||||
| typeof SuperTypes.ManualRedaction
|
|
||||||
| typeof SuperTypes.Recommendation
|
|
||||||
| typeof SuperTypes.Hint;
|
|
||||||
|
|
||||||
export const annotationEntityColorConfig: Record<EntityBasedColorType, 'hexColor' | 'recommendationHexColor' | 'skippedHexColor'> = {
|
|
||||||
[SuperTypes.IgnoredHint]: 'skippedHexColor',
|
[SuperTypes.IgnoredHint]: 'skippedHexColor',
|
||||||
[SuperTypes.Skipped]: 'skippedHexColor',
|
[SuperTypes.Skipped]: 'skippedHexColor',
|
||||||
[SuperTypes.Redaction]: 'hexColor',
|
[SuperTypes.Redaction]: 'hexColor',
|
||||||
[SuperTypes.ManualRedaction]: 'hexColor',
|
[SuperTypes.ManualRedaction]: 'hexColor',
|
||||||
|
[SuperTypes.ManualHint]: 'hexColor',
|
||||||
[SuperTypes.Recommendation]: 'recommendationHexColor',
|
[SuperTypes.Recommendation]: 'recommendationHexColor',
|
||||||
[SuperTypes.Hint]: 'hexColor',
|
[SuperTypes.Hint]: 'hexColor',
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Entity } from '@iqser/common-ui';
|
import { Entity } from '@iqser/common-ui';
|
||||||
import { IDictionary } from './dictionary';
|
|
||||||
import { DICTIONARY_TYPE_KEY_MAP, DictionaryType } from '../redaction-log';
|
|
||||||
import { List } from '@iqser/common-ui/lib/utils';
|
import { List } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { DICTIONARY_TYPE_KEY_MAP, DictionaryType } from '../redaction-log';
|
||||||
|
import { IDictionary } from './dictionary';
|
||||||
|
|
||||||
export class Dictionary extends Entity<IDictionary> implements IDictionary {
|
export class Dictionary extends Entity<IDictionary> implements IDictionary {
|
||||||
readonly addToDictionaryAction: boolean;
|
readonly addToDictionaryAction: boolean;
|
||||||
@ -24,7 +24,10 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
|
|||||||
falsePositiveEntries: List;
|
falsePositiveEntries: List;
|
||||||
falseRecommendationEntries: List;
|
falseRecommendationEntries: List;
|
||||||
|
|
||||||
constructor(entity: IDictionary, readonly virtual = false) {
|
constructor(
|
||||||
|
entity: IDictionary,
|
||||||
|
readonly virtual = false,
|
||||||
|
) {
|
||||||
super(entity);
|
super(entity);
|
||||||
this.addToDictionaryAction = !!entity.addToDictionaryAction;
|
this.addToDictionaryAction = !!entity.addToDictionaryAction;
|
||||||
this.caseInsensitive = !!entity.caseInsensitive;
|
this.caseInsensitive = !!entity.caseInsensitive;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
|
import { isProcessingStatuses, OCR_STATES, PENDING_STATES, PROCESSED_STATES, PROCESSING_STATES, ProcessingFileStatus } from '../files';
|
||||||
import { IDossierStats } from './dossier-stats';
|
import { IDossierStats } from './dossier-stats';
|
||||||
import { FileCountPerProcessingStatus, FileCountPerWorkflowStatus } from './types';
|
import { FileCountPerProcessingStatus, FileCountPerWorkflowStatus } from './types';
|
||||||
import { isProcessingStatuses, OCR_STATES, PENDING_STATES, PROCESSED_STATES, PROCESSING_STATES, ProcessingFileStatus } from '../files';
|
|
||||||
|
|
||||||
export const ProcessingTypes = {
|
export const ProcessingTypes = {
|
||||||
pending: 'pending',
|
pending: 'pending',
|
||||||
@ -21,7 +21,6 @@ export class DossierStats implements IDossierStats {
|
|||||||
readonly hasHintsNoRedactionsFilePresent: boolean;
|
readonly hasHintsNoRedactionsFilePresent: boolean;
|
||||||
readonly hasNoFlagsFilePresent: boolean;
|
readonly hasNoFlagsFilePresent: boolean;
|
||||||
readonly hasRedactionsFilePresent: boolean;
|
readonly hasRedactionsFilePresent: boolean;
|
||||||
readonly hasSuggestionsFilePresent: boolean;
|
|
||||||
readonly hasUpdatesFilePresent: boolean;
|
readonly hasUpdatesFilePresent: boolean;
|
||||||
readonly numberOfPages: number;
|
readonly numberOfPages: number;
|
||||||
readonly numberOfFiles: number;
|
readonly numberOfFiles: number;
|
||||||
@ -39,7 +38,6 @@ export class DossierStats implements IDossierStats {
|
|||||||
this.hasHintsNoRedactionsFilePresent = stats.hasHintsNoRedactionsFilePresent;
|
this.hasHintsNoRedactionsFilePresent = stats.hasHintsNoRedactionsFilePresent;
|
||||||
this.hasNoFlagsFilePresent = stats.hasNoFlagsFilePresent;
|
this.hasNoFlagsFilePresent = stats.hasNoFlagsFilePresent;
|
||||||
this.hasRedactionsFilePresent = stats.hasRedactionsFilePresent;
|
this.hasRedactionsFilePresent = stats.hasRedactionsFilePresent;
|
||||||
this.hasSuggestionsFilePresent = stats.hasSuggestionsFilePresent;
|
|
||||||
this.hasUpdatesFilePresent = stats.hasUpdatesFilePresent;
|
this.hasUpdatesFilePresent = stats.hasUpdatesFilePresent;
|
||||||
this.numberOfPages = stats.numberOfPages;
|
this.numberOfPages = stats.numberOfPages;
|
||||||
this.numberOfFiles = stats.numberOfFiles;
|
this.numberOfFiles = stats.numberOfFiles;
|
||||||
|
|||||||
@ -8,7 +8,6 @@ export interface IDossierStats {
|
|||||||
hasHintsNoRedactionsFilePresent: boolean;
|
hasHintsNoRedactionsFilePresent: boolean;
|
||||||
hasNoFlagsFilePresent: boolean;
|
hasNoFlagsFilePresent: boolean;
|
||||||
hasRedactionsFilePresent: boolean;
|
hasRedactionsFilePresent: boolean;
|
||||||
hasSuggestionsFilePresent: boolean;
|
|
||||||
hasUpdatesFilePresent: boolean;
|
hasUpdatesFilePresent: boolean;
|
||||||
numberOfPages: number;
|
numberOfPages: number;
|
||||||
numberOfFiles: number;
|
numberOfFiles: number;
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
import { Entity } from '@iqser/common-ui';
|
import { Entity } from '@iqser/common-ui';
|
||||||
|
import { ProcessingType, ProcessingTypes } from '../dossier-stats';
|
||||||
|
import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '../dossiers';
|
||||||
|
import { FileAttributes } from '../file-attributes';
|
||||||
import { StatusSorter } from '../shared';
|
import { StatusSorter } from '../shared';
|
||||||
|
import { IFile } from './file';
|
||||||
import {
|
import {
|
||||||
isFullProcessingStatuses,
|
isFullProcessingStatuses,
|
||||||
isProcessingStatuses,
|
isProcessingStatuses,
|
||||||
@ -11,10 +15,6 @@ import {
|
|||||||
WorkflowFileStatus,
|
WorkflowFileStatus,
|
||||||
WorkflowFileStatuses,
|
WorkflowFileStatuses,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { IFile } from './file';
|
|
||||||
import { FileAttributes } from '../file-attributes';
|
|
||||||
import { ARCHIVE_ROUTE, DOSSIERS_ROUTE } from '../dossiers';
|
|
||||||
import { ProcessingType, ProcessingTypes } from '../dossier-stats';
|
|
||||||
|
|
||||||
export class File extends Entity<IFile> implements IFile {
|
export class File extends Entity<IFile> implements IFile {
|
||||||
readonly added?: string;
|
readonly added?: string;
|
||||||
@ -51,7 +51,6 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
readonly rulesVersion?: number;
|
readonly rulesVersion?: number;
|
||||||
readonly uploader?: string;
|
readonly uploader?: string;
|
||||||
readonly excludedPages: number[];
|
readonly excludedPages: number[];
|
||||||
readonly hasSuggestions: boolean;
|
|
||||||
readonly processingStatus: ProcessingFileStatus;
|
readonly processingStatus: ProcessingFileStatus;
|
||||||
readonly workflowStatus: WorkflowFileStatus;
|
readonly workflowStatus: WorkflowFileStatus;
|
||||||
readonly fileManipulationDate: string;
|
readonly fileManipulationDate: string;
|
||||||
@ -83,7 +82,10 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
|
|
||||||
readonly processingType: ProcessingType;
|
readonly processingType: ProcessingType;
|
||||||
|
|
||||||
constructor(file: IFile, readonly reviewerName: string) {
|
constructor(
|
||||||
|
file: IFile,
|
||||||
|
readonly reviewerName: string,
|
||||||
|
) {
|
||||||
super(file);
|
super(file);
|
||||||
this.added = file.added;
|
this.added = file.added;
|
||||||
this.allManualRedactionsApplied = !!file.allManualRedactionsApplied;
|
this.allManualRedactionsApplied = !!file.allManualRedactionsApplied;
|
||||||
@ -122,7 +124,6 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
this.rulesVersion = file.rulesVersion;
|
this.rulesVersion = file.rulesVersion;
|
||||||
this.uploader = file.uploader;
|
this.uploader = file.uploader;
|
||||||
this.excludedPages = file.excludedPages || [];
|
this.excludedPages = file.excludedPages || [];
|
||||||
this.hasSuggestions = !!file.hasSuggestions;
|
|
||||||
this.fileManipulationDate = file.fileManipulationDate ?? '';
|
this.fileManipulationDate = file.fileManipulationDate ?? '';
|
||||||
this.redactionModificationDate = file.redactionModificationDate ?? '';
|
this.redactionModificationDate = file.redactionModificationDate ?? '';
|
||||||
this.lastManualChangeDate = file.lastManualChangeDate;
|
this.lastManualChangeDate = file.lastManualChangeDate;
|
||||||
@ -135,7 +136,7 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
this.statusSort = StatusSorter[this.workflowStatus];
|
this.statusSort = StatusSorter[this.workflowStatus];
|
||||||
this.cacheIdentifier = btoa(`${this.fileManipulationDate}${file.lastOCRTime}${file.ocrEndTime}${file.lastLayoutProcessed}`);
|
this.cacheIdentifier = btoa(`${this.fileManipulationDate}${file.lastOCRTime}${file.ocrEndTime}${file.lastLayoutProcessed}`);
|
||||||
this.hintsOnly = this.hasHints && !this.hasRedactions;
|
this.hintsOnly = this.hasHints && !this.hasRedactions;
|
||||||
this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
this.hasNone = !this.hasRedactions && !this.hasHints;
|
||||||
this.isProcessing = isProcessingStatuses.includes(this.processingStatus);
|
this.isProcessing = isProcessingStatuses.includes(this.processingStatus);
|
||||||
this.isFullProcessing = isFullProcessingStatuses.includes(this.processingStatus);
|
this.isFullProcessing = isFullProcessingStatuses.includes(this.processingStatus);
|
||||||
this.isOcrProcessing = this.processingStatus === ProcessingFileStatuses.OCR_PROCESSING;
|
this.isOcrProcessing = this.processingStatus === ProcessingFileStatuses.OCR_PROCESSING;
|
||||||
@ -144,7 +145,7 @@ export class File extends Entity<IFile> implements IFile {
|
|||||||
this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW;
|
this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW;
|
||||||
this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
|
this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
|
||||||
this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
|
this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
|
||||||
this.canBeApproved = !this.hasSuggestions && !this.isProcessing && !this.isError;
|
this.canBeApproved = !this.isProcessing && !this.isError;
|
||||||
this.canBeOpened = (!this.isError && !this.isUnprocessed && this.numberOfAnalyses > 0) || this.excluded;
|
this.canBeOpened = (!this.isError && !this.isUnprocessed && this.numberOfAnalyses > 0) || this.excluded;
|
||||||
this.canBeOCRed =
|
this.canBeOCRed =
|
||||||
!this.excluded &&
|
!this.excluded &&
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Object containing information on a specific file.
|
* Object containing information on a specific file.
|
||||||
*/
|
*/
|
||||||
import { ProcessingFileStatus, WorkflowFileStatus } from './types';
|
|
||||||
import { FileAttributes } from '../file-attributes';
|
import { FileAttributes } from '../file-attributes';
|
||||||
|
import { ProcessingFileStatus, WorkflowFileStatus } from './types';
|
||||||
|
|
||||||
export interface IFile {
|
export interface IFile {
|
||||||
/**
|
/**
|
||||||
@ -56,7 +56,6 @@ export interface IFile {
|
|||||||
* Shows if any requests were found during the analysis.
|
* Shows if any requests were found during the analysis.
|
||||||
*/
|
*/
|
||||||
readonly hasRequests?: boolean;
|
readonly hasRequests?: boolean;
|
||||||
readonly hasSuggestions?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* Shows if there is any change between the previous and current analysis.
|
* Shows if there is any change between the previous and current analysis.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,27 +1,77 @@
|
|||||||
import { ValuesOf } from '@iqser/common-ui/lib/utils';
|
import { ValuesOf } from '@iqser/common-ui/lib/utils';
|
||||||
|
import { EntityType, EntityTypes, EntryState, EntryStates, IEntityLogEntry } from '../redaction-log';
|
||||||
|
|
||||||
export const SuperTypes = {
|
export const SuperTypes = {
|
||||||
TextHighlight: 'text-highlight',
|
TextHighlight: 'text-highlight',
|
||||||
SuggestionChangeLegalBasis: 'suggestion-change-legal-basis',
|
|
||||||
SuggestionRecategorizeImage: 'suggestion-recategorize-image',
|
|
||||||
SuggestionAddDictionary: 'suggestion-add-dictionary',
|
|
||||||
SuggestionForceRedaction: 'suggestion-force-redaction',
|
|
||||||
SuggestionForceHint: 'suggestion-force-hint',
|
|
||||||
SuggestionResize: 'suggestion-resize',
|
|
||||||
SuggestionRemoveDictionary: 'suggestion-remove-dictionary',
|
|
||||||
SuggestionAdd: 'suggestion-add',
|
|
||||||
SuggestionRemove: 'suggestion-remove',
|
|
||||||
DeclinedSuggestion: 'suggestion-declined',
|
|
||||||
IgnoredHint: 'hint-ignored',
|
IgnoredHint: 'hint-ignored',
|
||||||
Skipped: 'skipped',
|
Skipped: 'skipped',
|
||||||
Redaction: 'redaction',
|
Redaction: 'redaction',
|
||||||
ManualRedaction: 'manual',
|
ManualRedaction: 'manual',
|
||||||
|
ManualHint: 'manual-hint',
|
||||||
Recommendation: 'recommendation',
|
Recommendation: 'recommendation',
|
||||||
Hint: 'hint',
|
Hint: 'hint',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type SuperType = ValuesOf<typeof SuperTypes>;
|
export type SuperType = ValuesOf<typeof SuperTypes>;
|
||||||
|
|
||||||
|
function wrongSuperTypeHandler(): never | undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://knecon.atlassian.net/wiki/spaces/RED/pages/102072322/EntityLog+-+Enum+combinations
|
||||||
|
*/
|
||||||
|
export const SuperTypeMapper: Record<EntityType, Record<EntryState, (entry: IEntityLogEntry) => SuperType | undefined>> = {
|
||||||
|
[EntityTypes.ENTITY]: {
|
||||||
|
[EntryStates.APPLIED]: entry => (entry.manualChanges.length ? SuperTypes.ManualRedaction : SuperTypes.Redaction),
|
||||||
|
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
|
||||||
|
[EntryStates.IGNORED]: () => SuperTypes.Redaction,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.HINT]: {
|
||||||
|
[EntryStates.APPLIED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.SKIPPED]: entry => (entry.manualChanges.length ? SuperTypes.ManualHint : SuperTypes.Hint),
|
||||||
|
[EntryStates.IGNORED]: () => SuperTypes.IgnoredHint,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.FALSE_POSITIVE]: {
|
||||||
|
[EntryStates.APPLIED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.RECOMMENDATION]: {
|
||||||
|
[EntryStates.APPLIED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.SKIPPED]: () => SuperTypes.Recommendation,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.FALSE_RECOMMENDATION]: {
|
||||||
|
[EntryStates.APPLIED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.AREA]: {
|
||||||
|
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
|
||||||
|
[EntryStates.SKIPPED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.IMAGE]: {
|
||||||
|
[EntryStates.APPLIED]: () => SuperTypes.Redaction,
|
||||||
|
[EntryStates.SKIPPED]: () => SuperTypes.Skipped,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
[EntityTypes.IMAGE_HINT]: {
|
||||||
|
[EntryStates.APPLIED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.SKIPPED]: () => SuperTypes.Hint,
|
||||||
|
[EntryStates.IGNORED]: wrongSuperTypeHandler,
|
||||||
|
[EntryStates.REMOVED]: wrongSuperTypeHandler,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const LowLevelFilterTypes = {
|
export const LowLevelFilterTypes = {
|
||||||
[SuperTypes.Hint]: true,
|
[SuperTypes.Hint]: true,
|
||||||
[SuperTypes.Redaction]: true,
|
[SuperTypes.Redaction]: true,
|
||||||
@ -36,27 +86,3 @@ export const FalsePositiveSuperTypes = {
|
|||||||
[SuperTypes.IgnoredHint]: true,
|
[SuperTypes.IgnoredHint]: true,
|
||||||
[SuperTypes.Skipped]: true,
|
[SuperTypes.Skipped]: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const SuggestionAddSuperTypes = {
|
|
||||||
[SuperTypes.SuggestionAdd]: true,
|
|
||||||
[SuperTypes.SuggestionAddDictionary]: true,
|
|
||||||
[SuperTypes.SuggestionForceRedaction]: true,
|
|
||||||
[SuperTypes.SuggestionForceHint]: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SuggestionRemoveSuperTypes = {
|
|
||||||
[SuperTypes.SuggestionRemove]: true,
|
|
||||||
[SuperTypes.SuggestionRemoveDictionary]: true,
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const SuggestionsSuperTypes = {
|
|
||||||
[SuperTypes.SuggestionAdd]: true,
|
|
||||||
[SuperTypes.SuggestionAddDictionary]: true,
|
|
||||||
[SuperTypes.SuggestionForceRedaction]: true,
|
|
||||||
[SuperTypes.SuggestionForceHint]: true,
|
|
||||||
[SuperTypes.SuggestionChangeLegalBasis]: true,
|
|
||||||
[SuperTypes.SuggestionRecategorizeImage]: true,
|
|
||||||
[SuperTypes.SuggestionRemove]: true,
|
|
||||||
[SuperTypes.SuggestionRemoveDictionary]: true,
|
|
||||||
[SuperTypes.SuggestionResize]: true,
|
|
||||||
} as const;
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export interface ILegalBasis {
|
export interface ILegalBasis {
|
||||||
name: string;
|
readonly name: string;
|
||||||
description?: string;
|
readonly description?: string;
|
||||||
reason?: string;
|
readonly reason?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
41
libs/red-domain/src/lib/redaction-log/entity-log-entry.ts
Normal file
41
libs/red-domain/src/lib/redaction-log/entity-log-entry.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { ITrackable } from '@iqser/common-ui';
|
||||||
|
import { IChange } from './change';
|
||||||
|
import { EntryState } from './entity-states';
|
||||||
|
import { EntityType } from './entity-types';
|
||||||
|
import { IManualChange } from './manual-change';
|
||||||
|
import { LogEntryEngine } from './types';
|
||||||
|
|
||||||
|
export interface IEntityLogEntryPosition {
|
||||||
|
readonly pageNumber: number;
|
||||||
|
readonly rectangle: readonly [number, number, number, number];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEntityLogEntry extends ITrackable {
|
||||||
|
type: string;
|
||||||
|
entryType: EntityType;
|
||||||
|
state: EntryState;
|
||||||
|
value: string;
|
||||||
|
reason: string;
|
||||||
|
matchedRule: string;
|
||||||
|
legalBasis: string;
|
||||||
|
imported: boolean;
|
||||||
|
containingNodeId: number[];
|
||||||
|
closestHeadline: string;
|
||||||
|
section: string;
|
||||||
|
color: number[];
|
||||||
|
positions: IEntityLogEntryPosition[];
|
||||||
|
textAfter?: string;
|
||||||
|
textBefore?: string;
|
||||||
|
startOffset?: number;
|
||||||
|
endOffset?: number;
|
||||||
|
imageHasTransparency: boolean;
|
||||||
|
dictionaryEntry: boolean;
|
||||||
|
dossierDictionaryEntry: boolean;
|
||||||
|
excluded: boolean;
|
||||||
|
changes: IChange[];
|
||||||
|
manualChanges: IManualChange[];
|
||||||
|
engines: LogEntryEngine[];
|
||||||
|
reference: string[];
|
||||||
|
importedRedactionIntersections: string[];
|
||||||
|
numberOfComments: number;
|
||||||
|
}
|
||||||
13
libs/red-domain/src/lib/redaction-log/entity-log.ts
Normal file
13
libs/red-domain/src/lib/redaction-log/entity-log.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ILegalBasis } from '../legal-basis';
|
||||||
|
import { IEntityLogEntry } from './entity-log-entry';
|
||||||
|
|
||||||
|
export interface IEntityLog {
|
||||||
|
analysisVersion: number;
|
||||||
|
analysisNumber: number;
|
||||||
|
entityLogEntry: IEntityLogEntry[];
|
||||||
|
legalBasis: ILegalBasis[];
|
||||||
|
dictionaryVersion: number;
|
||||||
|
dossierDictionaryVersion: number;
|
||||||
|
rulesVersion: number;
|
||||||
|
legalBasisVersion: number;
|
||||||
|
}
|
||||||
8
libs/red-domain/src/lib/redaction-log/entity-states.ts
Normal file
8
libs/red-domain/src/lib/redaction-log/entity-states.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export const EntryStates = {
|
||||||
|
APPLIED: 'APPLIED',
|
||||||
|
SKIPPED: 'SKIPPED',
|
||||||
|
IGNORED: 'IGNORED',
|
||||||
|
REMOVED: 'REMOVED',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EntryState = (typeof EntryStates)[keyof typeof EntryStates];
|
||||||
12
libs/red-domain/src/lib/redaction-log/entity-types.ts
Normal file
12
libs/red-domain/src/lib/redaction-log/entity-types.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const EntityTypes = {
|
||||||
|
ENTITY: 'ENTITY',
|
||||||
|
HINT: 'HINT',
|
||||||
|
FALSE_POSITIVE: 'FALSE_POSITIVE',
|
||||||
|
RECOMMENDATION: 'RECOMMENDATION',
|
||||||
|
FALSE_RECOMMENDATION: 'FALSE_RECOMMENDATION',
|
||||||
|
AREA: 'AREA',
|
||||||
|
IMAGE: 'IMAGE',
|
||||||
|
IMAGE_HINT: 'IMAGE_HINT',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export type EntityType = (typeof EntityTypes)[keyof typeof EntityTypes];
|
||||||
@ -12,3 +12,7 @@ export * from './recategorization.request';
|
|||||||
export * from './resize.request';
|
export * from './resize.request';
|
||||||
export * from './manual-change';
|
export * from './manual-change';
|
||||||
export * from './dictionary-entry-types';
|
export * from './dictionary-entry-types';
|
||||||
|
export * from './entity-log';
|
||||||
|
export * from './entity-log-entry';
|
||||||
|
export * from './entity-states';
|
||||||
|
export * from './entity-types';
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import { LogEntryStatus, ManualRedactionType } from './types';
|
import { ManualRedactionType } from './types';
|
||||||
|
|
||||||
export interface IManualChange {
|
export interface IManualChange {
|
||||||
userId: string;
|
userId: string;
|
||||||
processedDate: string;
|
processedDate: string;
|
||||||
requestedDate: string;
|
requestedDate: string;
|
||||||
annotationStatus: LogEntryStatus;
|
|
||||||
manualRedactionType: ManualRedactionType;
|
manualRedactionType: ManualRedactionType;
|
||||||
propertyChanges: { [key: string]: any };
|
propertyChanges: { [key: string]: any };
|
||||||
processed: boolean;
|
processed: boolean;
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { IRectangle } from '../geometry';
|
import { IRectangle } from '../geometry';
|
||||||
import { LogEntryStatus } from './types';
|
|
||||||
|
|
||||||
export interface IManualRedactionEntry {
|
export interface IManualRedactionEntry {
|
||||||
addToDictionary?: boolean;
|
addToDictionary?: boolean;
|
||||||
@ -11,7 +10,6 @@ export interface IManualRedactionEntry {
|
|||||||
reason?: string;
|
reason?: string;
|
||||||
requestDate?: string;
|
requestDate?: string;
|
||||||
softDeletedTime?: string;
|
softDeletedTime?: string;
|
||||||
status?: LogEntryStatus;
|
|
||||||
type?: string;
|
type?: string;
|
||||||
user?: string;
|
user?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
|
|||||||
@ -21,11 +21,3 @@ export const ManualRedactionTypes = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type ManualRedactionType = ValuesOf<typeof ManualRedactionTypes>;
|
export type ManualRedactionType = ValuesOf<typeof ManualRedactionTypes>;
|
||||||
|
|
||||||
export const LogEntryStatuses = {
|
|
||||||
APPROVED: 'APPROVED',
|
|
||||||
DECLINED: 'DECLINED',
|
|
||||||
REQUESTED: 'REQUESTED',
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export type LogEntryStatus = ValuesOf<typeof LogEntryStatuses>;
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user