diff --git a/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts b/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts index 56f3176a7..f6f00c713 100644 --- a/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts +++ b/apps/red-ui/src/app/models/file/annotation-permissions.utils.ts @@ -69,3 +69,5 @@ export const canEditHint = (annotation: AnnotationWrapper) => ((annotation.isHint && !annotation.isRuleBased) || annotation.isIgnoredHint) && !annotation.isImage; export const canEditImage = (annotation: AnnotationWrapper) => annotation.isImage; + +export const canRevertChanges = (annotation: AnnotationWrapper) => annotation.hasRedactionChanges; diff --git a/apps/red-ui/src/app/models/file/annotation.permissions.ts b/apps/red-ui/src/app/models/file/annotation.permissions.ts index 01aa08e8d..a9a8605af 100644 --- a/apps/red-ui/src/app/models/file/annotation.permissions.ts +++ b/apps/red-ui/src/app/models/file/annotation.permissions.ts @@ -17,6 +17,7 @@ import { canRemoveRedaction, canResizeAnnotation, canResizeInDictionary, + canRevertChanges, canUndo, } from './annotation-permissions.utils'; import { AnnotationWrapper } from './annotation.wrapper'; @@ -37,6 +38,7 @@ export class AnnotationPermissions { canEditAnnotations = true; canEditHints = true; canEditImages = true; + canRevertChanges = true; static forUser( isApprover: boolean, @@ -75,6 +77,7 @@ export class AnnotationPermissions { permissions.canEditAnnotations = canEditAnnotation(annotation); permissions.canEditHints = canEditHint(annotation); permissions.canEditImages = canEditImage(annotation); + permissions.canRevertChanges = canRevertChanges(annotation); summedPermissions._merge(permissions); } return summedPermissions; @@ -97,6 +100,7 @@ export class AnnotationPermissions { result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true); result.canEditHints = permissions.reduce((acc, next) => acc && next.canEditHints, true); result.canEditImages = permissions.reduce((acc, next) => acc && next.canEditImages, true); + result.canRevertChanges = permissions.reduce((acc, next) => acc && next.canRevertChanges, true); return result; } diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html index 3d08ac883..7cdc3c9e4 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-actions/annotation-actions.component.html @@ -151,6 +151,15 @@ icon="iqser:visibility" > + + ({ transform: value => value.filter(a => a !== undefined), }); @@ -68,6 +70,7 @@ export class AnnotationActionsComponent { readonly hideSkipped = computed(() => this.skippedService.hideSkipped() && this.annotations().some(a => a.isSkipped)); readonly isImageHint = computed(() => this.annotations().every(a => a.IMAGE_HINT)); readonly isImage = computed(() => this.annotations().reduce((acc, a) => acc && a.isImage, true)); + readonly canRevertChanges = computed(() => this.annotationPermissions().canRevertChanges); readonly annotationChangesAllowed = computed( () => (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.somePending(), ); @@ -104,13 +107,14 @@ export class AnnotationActionsComponent { readonly viewModeService: ViewModeService, readonly helpModeService: HelpModeService, readonly multiSelectService: MultiSelectService, + readonly skippedService: SkippedService, + readonly annotationActionsService: AnnotationActionsService, + readonly annotationReferencesService: AnnotationReferencesService, private readonly _state: FilePreviewStateService, private readonly _permissionsService: PermissionsService, private readonly _iqserPermissionsService: IqserPermissionsService, private readonly _annotationManager: REDAnnotationManager, - readonly skippedService: SkippedService, - readonly annotationActionsService: AnnotationActionsService, - readonly annotationReferencesService: AnnotationReferencesService, + private readonly _userPreferences: UserPreferenceService, ) {} get resized(): boolean { @@ -128,6 +132,11 @@ export class AnnotationActionsComponent { await this.annotationActionsService.convertRecommendationToAnnotation(annotations, 'accept'); } + async revertChanges() { + const annotations = untracked(this.annotations); + await this.annotationActionsService.revertChanges(annotations); + } + hideAnnotation() { const viewerAnnotations = untracked(this.viewerAnnotations); this._annotationManager.hide(viewerAnnotations); diff --git a/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.html b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.html new file mode 100644 index 000000000..fffc2d7d8 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.html @@ -0,0 +1,38 @@ +
+ + @for (column of config().columns; track column.label) { + + + + + } + + + + + + + + + + + + +
+ + + {{ cell[column.label] }} + + @if (!shouldBeExpanded()) { + @if (expandedElement === element) { + + } @else { + + } + } + +
+ +
+
+
diff --git a/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.scss b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.scss new file mode 100644 index 000000000..f40d06683 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.scss @@ -0,0 +1,101 @@ +table { + width: 100%; + + td { + max-width: 0; + } +} + +.table-container { + overflow-y: auto; + transition: height 0.25s ease-in-out; +} + +.mat-mdc-row { + height: 20px; + max-height: 20px; + font-size: 13px; + + &:nth-child(4n - 3), + &:nth-child(4n - 2) { + background-color: var(--iqser-alt-background); + } + + .mdc-data-table__cell { + height: 20px; + padding: 0 8px 0 8px; + border: none; + + &.expand-icon { + margin-right: 8px; + max-width: 28px; + width: 28px; + background: white; + } + + &.expanded-by-default { + width: 0; + margin: 0; + max-width: 0; + padding: 0; + } + + &.expanded { + width: 100%; + background: white; + padding: 0; + height: 0; + } + + .expanded-component { + width: 100%; + background: var(--iqser-alt-background); + } + } + + &.component-row { + height: 0; + } +} + +.mat-mdc-header-row { + height: 20px; + font-size: 13px; + + .mat-mdc-header-cell { + padding: 0; + border: none; + } +} + +mat-icon { + width: 20px; + height: 20px; + font-size: 13px; +} + +.expanded-component { + overflow: hidden; + display: flex; + + &:not(.expanded-by-default) { + margin-left: 28px; + } +} + +label { + opacity: 0.7; + font-weight: normal; + padding-left: 8px; +} + +.cell { + text-align: start; + white-space: nowrap; + text-overflow: ellipsis; + list-style-position: inside; + overflow: hidden; + + padding-right: 8px; + line-height: 1.5; +} diff --git a/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.ts b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.ts new file mode 100644 index 000000000..44cf24940 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/components/expandable-row-table/expandable-row-table.component.ts @@ -0,0 +1,117 @@ +import { Component, computed, effect, input, signal, Type, viewChildren, ViewContainerRef } from '@angular/core'; +import { NgStyle } from '@angular/common'; +import { + MatCell, + MatCellDef, + MatColumnDef, + MatHeaderCell, + MatHeaderCellDef, + MatHeaderRow, + MatHeaderRowDef, + MatRow, + MatRowDef, + MatTable, +} from '@angular/material/table'; +import { MatIcon } from '@angular/material/icon'; +import { isJustOne } from '@common-ui/utils'; + +export interface Data { + styles?: string; + component?: Type; + componentInputs?: { [key: string]: unknown }; + expanded: boolean; + values: Record; +} + +export interface Column { + label: string; + value: string; + width?: string; +} + +export interface Config { + columns: Column[]; + data: Data[]; +} + +const TABLE_ROW_SIZE = 24; +const MAX_ITEMS_DISPLAY = 10; + +@Component({ + selector: 'redaction-expandable-row-table', + standalone: true, + imports: [ + NgStyle, + MatTable, + MatColumnDef, + MatHeaderCell, + MatCell, + MatIcon, + MatHeaderRow, + MatRow, + MatHeaderCellDef, + MatCellDef, + MatHeaderRowDef, + MatRowDef, + ], + templateUrl: './expandable-row-table.component.html', + styleUrl: './expandable-row-table.component.scss', +}) +export class ExpandableRowTableComponent { + readonly config = input.required(); + readonly source = computed(() => this.config().data.map(row => row.values)); + readonly columnsToDisplay = computed(() => ['expand-icon', ...this.config().columns.map(column => column.label)]); + readonly detailsComponentRef = viewChildren('detailsComponent', { read: ViewContainerRef }); + readonly shouldBeExpanded = computed(() => isJustOne(this.config().data)); + readonly redactedTextsAreaHeight = computed(() => + this.shouldBeExpanded() + ? 'unset' + : `${ + (this.config().data.length <= MAX_ITEMS_DISPLAY + ? TABLE_ROW_SIZE * this.config().data.length + (this.#currentExpandedComponentHeight() ?? 0) + : TABLE_ROW_SIZE * MAX_ITEMS_DISPLAY) + 20 + }px`, + ); + expandedElement: Record; + #expandedComponentRef = null; + readonly #currentExpandedComponentHeight = signal(null); + + constructor() { + effect(() => { + if (this.shouldBeExpanded()) { + this.#initializeDetailsComponent(this.source()[0]); + } + }); + } + + expand($event: MouseEvent, element: Record) { + this.expandedElement = element; + this.#initializeDetailsComponent(element); + $event.stopPropagation(); + } + + close($event: MouseEvent) { + this.expandedElement = null; + this.#currentExpandedComponentHeight.set(null); + this.detailsComponentRef().forEach(ref => ref.clear()); + $event.stopPropagation(); + } + + #initializeDetailsComponent(element: Record) { + this.detailsComponentRef().forEach(ref => ref.clear()); + const expandedIndex = this.source().indexOf(element); + if (this.detailsComponentRef()[expandedIndex]) { + this.#currentExpandedComponentHeight.set(null); + const config = this.config().data.find(row => row.values['id'] === element['id']); + this.#expandedComponentRef = this.detailsComponentRef()[expandedIndex].createComponent(config.component); + if (config.componentInputs) { + for (const [key, value] of Object.entries(config.componentInputs)) { + (this.#expandedComponentRef.instance as any)[key] = value; + } + } + setTimeout(() => + this.#currentExpandedComponentHeight.set(this.#expandedComponentRef.instance.elementRef.nativeElement.clientHeight), + ); + } + } +} diff --git a/apps/red-ui/src/app/modules/file-preview/components/manual-changes/manual-changes.component.html b/apps/red-ui/src/app/modules/file-preview/components/manual-changes/manual-changes.component.html index 574a6204d..0fe68aacc 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/manual-changes/manual-changes.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/manual-changes/manual-changes.component.html @@ -1,10 +1,4 @@
-

-
-

{{ redaction().value }}

-

{{ redaction().typeLabel }}

-

{{ redaction().legalBasis }}

-

[ - { label: change.manualRedactionType }, + { label: change.manualRedactionType.toLowerCase().capitalize() }, { label: change.requestedDate, componentType: 'date' }, { label: change.userId, componentType: 'avatar' }, ] as ValueColumn[], ), ); + readonly elementRef = inject(ElementRef); } diff --git a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.html b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.html index a801712b9..7e09e2fcd 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.html +++ b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.html @@ -1,13 +1,19 @@
@for (column of _columns(); track column.label) { -
+
} @for (row of _data(); track $index) { @for (cell of row; track cell.label) {
- {{ cell.label }} + @if (cell.componentType === 'date') { + {{ cell.label | date }} + } @else if (cell.componentType === 'avatar') { + + } @else { + {{ cell.label }} + }
} } diff --git a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.scss b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.scss index 4c49205c1..1c930ff0e 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.scss +++ b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.scss @@ -11,7 +11,10 @@ position: sticky; top: 0; z-index: 1; - background: white; + + &:not(.background) { + background: white; + } label { opacity: 0.7; @@ -26,6 +29,10 @@ list-style-position: inside; overflow: hidden; + &:not(.background) { + background: white; + } + padding-right: 8px; line-height: 1.5; } diff --git a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.ts b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.ts index ec5a80d64..9e11b0940 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/selected-annotations-table/selected-annotations-table.component.ts @@ -1,11 +1,14 @@ import { Component, computed, input } from '@angular/core'; import { NgClass, NgStyle } from '@angular/common'; +import { InitialsAvatarComponent } from '@common-ui/users'; +import { DatePipe } from '@shared/pipes/date.pipe'; export interface ValueColumn { label: string; hide?: boolean; bold?: boolean; width?: string; + componentType?: 'string' | 'avatar' | 'date'; } const TABLE_ROW_SIZE = 20; @@ -13,13 +16,13 @@ const MAX_ITEMS_DISPLAY = 10; @Component({ selector: 'redaction-selected-annotations-table', - imports: [NgClass, NgStyle], + imports: [NgClass, NgStyle, InitialsAvatarComponent, DatePipe], templateUrl: './selected-annotations-table.component.html', styleUrl: './selected-annotations-table.component.scss', }) export class SelectedAnnotationsTableComponent { readonly defaultColumnWidth = input(false); - + readonly headerHasBackground = input(false); readonly columns = input.required(); readonly _columns = computed(() => this.columns().filter(item => !this.defaultColumnWidth() || !item.hide)); diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.html b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.html new file mode 100644 index 000000000..ba357998e --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.html @@ -0,0 +1,22 @@ +
+
+ +
+ +
+
+ +
+
+ +
+ +
+
+ + +
diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.scss b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.scss new file mode 100644 index 000000000..f2c1c270b --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.scss @@ -0,0 +1,3 @@ +.dialog-content:not(.is-expanded) { + padding-left: 20px; +} diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.ts new file mode 100644 index 000000000..f4c41fccf --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component.ts @@ -0,0 +1,50 @@ +import { Component, signal } from '@angular/core'; +import { CircleButtonComponent, IconButtonComponent, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui'; +import { RevertManualChangesData } from '../../utils/dialog-types'; +import { TranslateModule } from '@ngx-translate/core'; +import { Config, ExpandableRowTableComponent } from '../../components/expandable-row-table/expandable-row-table.component'; +import { ManualChangesComponent } from '../../components/manual-changes/manual-changes.component'; +import { MatDialogClose } from '@angular/material/dialog'; +import { isJustOne } from '@common-ui/utils'; + +@Component({ + selector: 'redaction-revert-manual-changes-dialog', + standalone: true, + imports: [IconButtonComponent, TranslateModule, CircleButtonComponent, ExpandableRowTableComponent, MatDialogClose], + templateUrl: './revert-manual-changes-dialog.component.html', + styleUrl: './revert-manual-changes-dialog.component.scss', +}) +export class RevertManualChangesDialogComponent extends IqserDialogComponent { + protected readonly IconButtonTypes = IconButtonTypes; + + readonly config: Config = { + columns: [ + { label: 'value', value: 'Value', width: '25%' }, + { label: 'type', value: 'Type', width: '25%' }, + { + label: 'reason', + value: 'Reason', + width: '50%', + }, + ], + data: this.data.redactions.map(redaction => ({ + values: { + id: redaction.id, + value: redaction.value, + type: redaction.typeLabel, + reason: redaction.legalBasisValue, + }, + expanded: false, + component: ManualChangesComponent, + componentInputs: { redaction: signal(redaction), isExpanded: signal(isJustOne(this.data.redactions)) }, + })), + }; + + constructor() { + super(); + } + + save() { + this.close(true); + } +} diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts index 2ee928182..635acbf3d 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-actions.service.ts @@ -43,6 +43,7 @@ import { RemoveRedactionPermissions, RemoveRedactionResult, ResizeRedactionData, + RevertManualChangesData, } from '../utils/dialog-types'; import { toPosition } from '../utils/pdf-calculation.utils'; import { FileDataService } from './file-data.service'; @@ -50,6 +51,7 @@ import { FilePreviewDialogService } from './file-preview-dialog.service'; import { FilePreviewStateService } from './file-preview-state.service'; import { ManualRedactionService } from './manual-redaction.service'; import { SkippedService } from './skipped.service'; +import { RevertManualChangesDialogComponent } from '../dialogs/revert-manual-changes-dialog/revert-manual-changes-dialog.component'; @Injectable() export class AnnotationActionsService { @@ -68,6 +70,7 @@ export class AnnotationActionsService { private readonly _skippedService: SkippedService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _permissionsService: PermissionsService, + private readonly _iqserDialogService: IqserDialog, ) {} removeHighlights(highlights: AnnotationWrapper[]): void { @@ -213,6 +216,22 @@ export class AnnotationActionsService { } } + async revertChanges(annotations: AnnotationWrapper[]) { + const dialogData: RevertManualChangesData = { + redactions: annotations, + }; + const result = await this._iqserDialogService.openDefault(RevertManualChangesDialogComponent, { data: dialogData }).result(); + if (!result) return; + + const data = annotations.map(annotation => annotation.id); + const dossierId = this._state.dossierId; + const fileId = this._state.fileId; + + this.#processObsAndEmit( + this._manualRedactionService.revertChanges(data, dossierId, fileId, this._state.file().excludedFromAutomaticAnalysis), + ).then(); + } + undoDirectAction(annotations: AnnotationWrapper[]) { const { dossierId, fileId } = this._state; const modifyDictionary = annotations[0].isModifyDictionary; diff --git a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index 524717c07..23172e4bc 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -1,7 +1,7 @@ import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { GenericService, IqserPermissionsService, Toaster } from '@iqser/common-ui'; +import { GenericService, IqserPermissionsService, QueryParam, Toaster } from '@iqser/common-ui'; import { List } from '@iqser/common-ui/lib/utils'; import { type AnnotationWrapper } from '@models/file/annotation.wrapper'; import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper'; @@ -108,6 +108,10 @@ export class ManualRedactionService extends GenericService { return this.undo(annotationIds, dossierId, fileId).pipe(this.#showToast('undo', modifyDictionary)); } + revertChanges(body: List, dossierId: string, fileId: string, includeOnlyUnprocessed: boolean) { + return this.#revertChanges(body, dossierId, fileId, includeOnlyUnprocessed).pipe(this.#showToast('revert-changes')); + } + removeRedaction( body: List | IBulkLocalRemoveRequest, dossierId: string, @@ -154,6 +158,14 @@ export class ManualRedactionService extends GenericService { return this._post(body, `${this.#bulkRedaction}/resize/${dossierId}/${fileId}`).pipe(this.#log('Resize', body)); } + #revertChanges(body: List, dossierId: string, fileId: string, includeOnlyUnprocessed: boolean) { + const queryParams: List = [ + { key: 'includeOnlyUnprocessed', value: includeOnlyUnprocessed }, + { key: 'includeOnlyLocal', value: true }, + ]; + return this.delete(body, `${this._defaultModelPath}/bulk/undo/${dossierId}/${fileId}`, queryParams); + } + #recategorize( body: List | IBulkRecategorizationRequest, dossierId: string, diff --git a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts index 0fa78c9eb..e299f5815 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/pdf-annotation-actions.service.ts @@ -10,6 +10,7 @@ import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manag import { AnnotationActionsService } from './annotation-actions.service'; import { FilePreviewStateService } from './file-preview-state.service'; import { UI_ROOT_PATH_FN } from '@common-ui/utils'; +import { UserPreferenceService } from '@users/user-preference.service'; @Injectable() export class PdfAnnotationActionsService { @@ -20,8 +21,10 @@ export class PdfAnnotationActionsService { readonly #annotationActionsService = inject(AnnotationActionsService); readonly #iqserPermissionsService = inject(IqserPermissionsService); readonly #annotationManager = inject(REDAnnotationManager); + readonly #userPreferences = inject(UserPreferenceService); readonly #isDocumine = getConfig().IS_DOCUMINE; readonly #convertPath = inject(UI_ROOT_PATH_FN); + readonly #devMode = this.#userPreferences.isIqserDevMode; get(annotations: AnnotationWrapper[], annotationChangesAllowed: boolean): IHeaderElement[] { const availableActions: IHeaderElement[] = []; @@ -97,6 +100,13 @@ export class PdfAnnotationActionsService { availableActions.push(forceHintButton); } + if (permissions.canRevertChanges && annotationChangesAllowed && this.#devMode) { + const revertChangesButton = this.#getButton('general/revert-changes', _('annotation-actions.revert-changes.label'), () => + this.#annotationActionsService.revertChanges(annotations), + ); + availableActions.push(revertChangesButton); + } + if (permissions.canRemoveRedaction && sameType && annotationChangesAllowed) { const removeRedactionButton = this.#getButton('trash', _('annotation-actions.remove-annotation.remove-redaction'), () => this.#annotationActionsService.removeRedaction(annotations, permissions), diff --git a/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts b/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts index 0a697fd0a..9d3a11ceb 100644 --- a/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts +++ b/apps/red-ui/src/app/modules/file-preview/utils/dialog-types.ts @@ -183,3 +183,7 @@ export interface RectangleDialogData { export interface RectangleDialogResult { annotation: IManualRedactionEntry; } + +export interface RevertManualChangesData { + redactions: AnnotationWrapper[]; +} diff --git a/apps/red-ui/src/app/modules/icons/icons.module.ts b/apps/red-ui/src/app/modules/icons/icons.module.ts index 092a67d2a..6eb701249 100644 --- a/apps/red-ui/src/app/modules/icons/icons.module.ts +++ b/apps/red-ui/src/app/modules/icons/icons.module.ts @@ -75,6 +75,7 @@ export class IconsModule { 'reference', 'remove-from-dict', 'report', + 'revert-changes', 'rotation', 'rss', 'rule', diff --git a/apps/red-ui/src/app/translations/annotation-actions-translations.ts b/apps/red-ui/src/app/translations/annotation-actions-translations.ts index b08871dea..3b8d2bd07 100644 --- a/apps/red-ui/src/app/translations/annotation-actions-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-actions-translations.ts @@ -44,6 +44,10 @@ export const manualRedactionActionsTranslations: Record{count} {count, plural, one{Benutzer} other {Benutzer}}" }, + "dossier-templates": { + "label": "Dossier-Vorlagen", + "status": { + "active": "Aktiv", + "inactive": "Inaktiv", + "incomplete": "Unvollständig" + } + }, "dossier-templates-listing": { "action": { "clone": "Vorlage klonen", @@ -1142,14 +1157,6 @@ "title": "{length} {length, plural, one{Dossier-Vorlage} other{Dossier-Vorlagen}}" } }, - "dossier-templates": { - "label": "Dossier-Vorlagen", - "status": { - "active": "Aktiv", - "inactive": "Inaktiv", - "incomplete": "Unvollständig" - } - }, "dossier-watermark-selector": { "heading": "Wasserzeichen auf Dokumenten", "no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:
Bitten Sie Ihren Admin, eines zu konfigurieren.", @@ -1360,6 +1367,15 @@ "title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}" } }, + "entity": { + "info": { + "actions": { + "revert": "Zurücksetzen", + "save": "Änderungen speichern" + }, + "heading": "Entität bearbeiten" + } + }, "entity-rules-screen": { "error": { "generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen." @@ -1373,28 +1389,19 @@ "title": "Entitätsregeln-Editor", "warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden" }, - "entity": { - "info": { - "actions": { - "revert": "Zurücksetzen", - "save": "Änderungen speichern" - }, - "heading": "Entität bearbeiten" - } - }, "error": { "deleted-entity": { "dossier": { "action": "Zurück zur Übersicht", "label": "Dieses Dossier wurde gelöscht!" }, - "file-dossier": { - "action": "Zurück zur Übersicht", - "label": "Das Dossier dieser Datei wurde gelöscht!" - }, "file": { "action": "Zurück zum Dossier", "label": "Diese Datei wurde gelöscht!" + }, + "file-dossier": { + "action": "Zurück zur Übersicht", + "label": "Das Dossier dieser Datei wurde gelöscht!" } }, "file-preview": { @@ -1412,6 +1419,12 @@ }, "exact-date": "{day}. {month} {year} um {hour}:{minute} Uhr", "file": "Datei", + "file-attribute": { + "update": { + "error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.", + "success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert." + } + }, "file-attribute-encoding-types": { "ascii": "ASCII", "iso": "ISO-8859-1", @@ -1422,12 +1435,6 @@ "number": "Nummer", "text": "Freier Text" }, - "file-attribute": { - "update": { - "error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.", - "success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert." - } - }, "file-attributes-configurations": { "cancel": "Abbrechen", "form": { @@ -1645,15 +1652,6 @@ "zip": "Die Zip-Datei wurde erfolgreich hochgeladen!" } }, - "filter-menu": { - "filter-options": "Filteroptionen", - "filter-types": "Filter", - "label": "Filter", - "pages-without-annotations": "Nur Seiten ohne Annotationen", - "redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen", - "unseen-pages": "Nur Annotationen auf ungesehenen Seiten", - "with-comments": "Nur Annotationen mit Kommentaren" - }, "filter": { "analysis": "Analyse erforderlich", "comment": "Kommentare", @@ -1663,6 +1661,15 @@ "redaction": "Schwärzung", "updated": "Aktualisiert" }, + "filter-menu": { + "filter-options": "Filteroptionen", + "filter-types": "Filter", + "label": "Filter", + "pages-without-annotations": "Nur Seiten ohne Annotationen", + "redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen", + "unseen-pages": "Nur Annotationen auf ungesehenen Seiten", + "with-comments": "Nur Annotationen mit Kommentaren" + }, "filters": { "assigned-people": "Bearbeiter", "documents-status": "Dokumentenstatus", @@ -1952,6 +1959,13 @@ "user-promoted-to-approver": "Sie wurden zum Genehmiger in einem Dossier ernannt: {dossierHref, select, null{{dossierName}} other{{dossierName}}}", "user-removed-as-dossier-member": "Sie wurden als Dossier-Mitglied entfernt: \n{dossierHref, select, null{{dossierName}} other\n{{dossierName}}}\n" }, + "notifications": { + "button-text": "Benachrichtigungen", + "deleted-dossier": "Gelöschtes Dossier", + "label": "Benachrichtigungen", + "mark-all-as-read": "Alle als gelesen markieren", + "mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren" + }, "notifications-screen": { "category": { "email-notifications": "E-Mail-Benachrichtigungen", @@ -1965,7 +1979,6 @@ "dossier": "Benachrichtigungen zu Dossiers", "other": "Andere Benachrichtigungen" }, - "options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten", "options": { "ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen werde", "ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde", @@ -1983,6 +1996,7 @@ "USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde", "USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere" }, + "options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten", "schedule": { "daily": "Tägliche Zusammenfassung", "instant": "Sofort", @@ -1990,13 +2004,6 @@ }, "title": "Benachrichtigungseinstellungen" }, - "notifications": { - "button-text": "Benachrichtigungen", - "deleted-dossier": "Gelöschtes Dossier", - "label": "Benachrichtigungen", - "mark-all-as-read": "Alle als gelesen markieren", - "mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren" - }, "ocr": { "confirmation-dialog": { "cancel": "Abbrechen", @@ -2108,6 +2115,10 @@ "warnings-label": "Dialoge und Meldungen", "warnings-subtitle": "„Nicht mehr anzeigen“-Optionen" }, + "processing": { + "basic": "Verarbeitung läuft", + "ocr": "OCR" + }, "processing-status": { "ocr": "OCR", "pending": "Ausstehend", @@ -2115,10 +2126,6 @@ "processed": "Verarbeitet", "processing": "Verarbeitung läuft" }, - "processing": { - "basic": "Verarbeitung läuft", - "ocr": "OCR" - }, "readonly": "Lesemodus", "readonly-archived": "Lesemodus (archiviert)", "redact-text": { @@ -2368,6 +2375,16 @@ "header": "Größe von {type} ändern" } }, + "revert-manual-changes-dialog": { + "actions": { + "cancel": "Abbrechen", + "save": "Zu den Originalen zurückkehren" + }, + "details": { + "title": "Manuelle Änderungen" + }, + "title": "Lokale manuelle Änderungen rückgängig machen" + }, "revert-value-dialog": { "actions": { "cancel": "Abbrechen", @@ -2388,6 +2405,12 @@ "red-user-admin": "{count, plural, one{Benutzeradmin} other{Benutzeradmins}}", "regular": "{count, plural, one{regulärer Benutzer} other{reguläre Benutzer}}" }, + "search": { + "active-dossiers": "Dokumente in aktiven Dossiers", + "all-dossiers": "Alle Dokumente", + "placeholder": "Dokumente durchsuchen...", + "this-dossier": "In diesem Dossier" + }, "search-screen": { "cols": { "assignee": "Bearbeiter", @@ -2411,12 +2434,6 @@ "no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.", "table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}" }, - "search": { - "active-dossiers": "Dokumente in aktiven Dossiers", - "all-dossiers": "Alle Dokumente", - "placeholder": "Dokumente durchsuchen...", - "this-dossier": "In diesem Dossier" - }, "seconds": "Sekunden", "size": "Größe", "smtp-auth-config": { diff --git a/apps/red-ui/src/assets/i18n/redact/en.json b/apps/red-ui/src/assets/i18n/redact/en.json index 90519f397..5ad618d8a 100644 --- a/apps/red-ui/src/assets/i18n/redact/en.json +++ b/apps/red-ui/src/assets/i18n/redact/en.json @@ -338,6 +338,10 @@ "error": "Failed to remove redaction: {error}", "success": "Redaction removed" }, + "revert-changes": { + "error": "Failed to revert manual changes: {error}", + "success": "Reverted manual changes successfully." + }, "undo": { "error": "Failed to undo: {error}", "success": "Undo successful" @@ -359,6 +363,9 @@ "resize": { "label": "Resize" }, + "revert-changes": { + "label": "Revert changes" + }, "see-references": { "label": "See references" }, @@ -2368,6 +2375,16 @@ "header": "Resize {type}" } }, + "revert-manual-changes-dialog": { + "actions": { + "cancel": "Cancel", + "save": "Revert to originals" + }, + "details": { + "title": "Manual changes" + }, + "title": "Revert local manual changes" + }, "revert-value-dialog": { "actions": { "cancel": "Cancel", diff --git a/apps/red-ui/src/assets/i18n/scm/de.json b/apps/red-ui/src/assets/i18n/scm/de.json index 350f97a69..92fb5c9ef 100644 --- a/apps/red-ui/src/assets/i18n/scm/de.json +++ b/apps/red-ui/src/assets/i18n/scm/de.json @@ -225,7 +225,7 @@ "actions": { "cancel": "Abbrechen", "save": "Speichern", - "save-and-remember": "Save and remember my choice" + "save-and-remember": "Speichern und Auswahl merken" }, "content": { "comment": "Kommentar", @@ -243,7 +243,7 @@ }, "type": "Typ", "type-placeholder": "Typ auswählen...", - "value": "Wert\n" + "value": "Wert" }, "title": "Hinweis hinzufügen" } @@ -275,6 +275,9 @@ "watermarks": "Wasserzeichen" }, "analysis-disabled": "Analyse deaktiviert", + "annotation": { + "pending": "(Analyse steht aus)" + }, "annotation-actions": { "accept-recommendation": { "label": "Empfehlung annehmen" @@ -330,13 +333,17 @@ "error": "Rekategorisierung des Bilds fehlgeschlagen: {error}", "success": "Bild wurde einer neuen Kategorie zugeordnet." }, + "remove": { + "error": "Entfernen der Annotation fehlgeschlagen: {error}", + "success": "Annotation wurde entfernt" + }, "remove-hint": { "error": "Entfernen des Hinweises fehlgeschlagen: {error}", "success": "Hinweis wurde entfernt" }, - "remove": { - "error": "Entfernen der Annotation fehlgeschlagen: {error}", - "success": "Annotation wurde entfernt" + "revert-changes": { + "error": "Manuelle Änderungen konnten nicht rückgängig gemacht werden: {error}", + "success": "Manuelle Änderungen wurden erfolgreich rückgängig gemacht." }, "undo": { "error": "Die Aktion konnte nicht rückgängig gemacht werden. Fehler: {error}", @@ -350,14 +357,17 @@ "remove-highlights": { "label": "Ausgewählte Markierungen entfernen" }, + "resize": { + "label": "Größe ändern" + }, "resize-accept": { "label": "Neue Größe speichern" }, "resize-cancel": { "label": "Größenänderung abbrechen" }, - "resize": { - "label": "Größe ändern" + "revert-changes": { + "label": "Änderungen rückgängig machen" }, "see-references": { "label": "Referenzen anzeigen" @@ -375,7 +385,7 @@ "removed-manual": "Schwärzung/Hinweis entfernt", "resized": "Schwärzungsbereich wurde geändert" }, - "annotation-content": "{hasRule, select, true {Rule {matchedRule} trifft zu:{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n Abschnitt{sectionSymbol} \"{section}\"} other {}}", + "annotation-content": "{hasRule, select, true {Rule {matchedRule} trifft zu:{ruleSymbol}} other {}} {hasReason, select, true {{reason}} other {}} {hasLb, select, true {Legal basis: {legalBasis}} other {}} {hasOverride, select, true {Removed by manual override} other {}} {hasSection, select, true {{shouldLower, plural, =0 {I} other {i}}n Abschnitt{sectionSymbol} \\\"{section}\\\"} other {}}", "annotation-engines": { "dictionary": "{isHint, select, true{Hinweis} other{Schwärzung}} basiert auf Wörterbuch", "dossier-dictionary": "Annotation basiert auf Dossier-Wörterbuch", @@ -393,9 +403,6 @@ "skipped": "Übersprungen", "text-highlight": "Markierung" }, - "annotation": { - "pending": "(Analyse steht aus)" - }, "annotations": "Annotationen", "archived-dossiers-listing": { "no-data": { @@ -525,11 +532,11 @@ "add-title": "Neue Definition hinzufügen", "columns": { "name": "Name", - "position": "Pos." + "position": "pos." }, "edit-title": "Definition von {displayName} bearbeiten", "form": { - "autogenerated-label": "Wird ausgehend vom ersten Anzeigenamen automatisch generiert.", + "autogenerated-label": "Wurde ausgehend vom initialen Anzeigenamen automatisch generiert", "description": "Beschreibung", "description-placeholder": "Beschreibung", "display-name": "Anzeigename", @@ -571,7 +578,7 @@ }, "search": "Nach Name suchen...", "table-col-names": { - "column-labels": "Spaltenbeschriftungen", + "column-labels": "Column labels", "name": "Name", "number-of-lines": "Zeilenzahl", "version": "Version" @@ -614,7 +621,7 @@ "impacted-report": "{reportsCount} Berichte nutzen den Platzhalter dieses Attributs. Bitte aktualisieren Sie diese.", "title": "{count, plural, one{Attribut} other{Attribute}} löschen", "toast-error": "Bitte bestätigen Sie, dass die Folgen dieser Aktion verstehen.", - "warning": "Warnung: Wiederherstellung des Attributs nicht möglich." + "warning": "Warnung: Aktion kann nicht rückgängig gemacht werden." }, "confirm-delete-dossier-state": { "cancel": "Abbrechen", @@ -636,18 +643,18 @@ "impacted-dossiers": "{dossiersCount} {dossiersCount, plural, one{Dossier} other{Dossiers}} sind betroffen", "title": "{usersCount, plural, one{Benutzer} other{Benutzer}} aus Workspace entfernen", "toast-error": "Bitte bestätigen Sie, dass Sie die Folgen dieser Aktion verstehen.", - "warning": "Warnung: Wiederherstellung des Benutzers nicht möglich." + "warning": "Warnung: Aktion kann nicht rückgängig gemacht werden." }, "confirmation-dialog": { "approve-file": { - "confirmationText": "Trotzdem genehmigen", + "confirmationText": "Dennoch freigeben", "denyText": "Nein, abbrechen", "question": "Dieses Dokument enthält ungesehene Änderungen, die sich durch die Reanalyse ergeben haben.

Möchten Sie es trotzdem freigeben?", "title": "Warnung!", "warning-reason": { - "legal-basis-missing": "Rechtsgrundlage fehlt", - "pending-changes": "Änderungen stehen aus", - "unmapped-justification": "Nicht gemappte Begründung" + "legal-basis-missing": "Legal basis missing", + "pending-changes": "Ausstehende Änderungen", + "unmapped-justification": "Unmapped justification" } }, "assign-file-to-me": { @@ -703,7 +710,7 @@ }, "content": "Grund", "copilot": { - "label": "" + "label": "Copilot" }, "dashboard": { "empty-template": { @@ -931,7 +938,7 @@ "action": "Ganzes Dossier analysieren" }, "rules": { - "timeoutError": "Regeln für Dossier-Vorlagen gesperrt!" + "timeoutError": "Regeln der Dossier-Vorlage gesperrt!" }, "stats": { "analyzed-pages": "{count, plural, one{Seite} other{Seiten}}", @@ -948,7 +955,7 @@ "table-header": { "title": "{length} {length, plural, one{aktives Dossier} other{aktive Dossiers}}" }, - "template-inactive": "Aktuell ausgewählte Dossier-Vorlage inaktiv!" + "template-inactive": "Die aktuell ausgewählte Dossier-Vorlage ist inaktiv." }, "dossier-overview": { "approve": "Genehmigen", @@ -1026,13 +1033,13 @@ "recent": "Neu ({hours} h)", "unassigned": "Niemandem zugewiesen" }, + "reanalyse": { + "action": "Datei analysieren" + }, "reanalyse-dossier": { "error": "Die Dateien konnten nicht für eine Reanalyse eingeplant werden. Bitte versuchen Sie es erneut.", "success": "Dateien für Reanalyse vorgesehen." }, - "reanalyse": { - "action": "Datei analysieren" - }, "report-download": "Bericht herunterladen", "start-auto-analysis": "Auto-Analyse aktivieren", "stop-auto-analysis": "Auto-Analyse anhalten", @@ -1108,6 +1115,14 @@ "total-documents": "Dokumente", "total-people": "{count} {count, plural, one{Benutzer} other {Benutzer}}" }, + "dossier-templates": { + "label": "Dossier-Vorlagen", + "status": { + "active": "Aktiv", + "inactive": "Inaktiv", + "incomplete": "Unvollständig" + } + }, "dossier-templates-listing": { "action": { "clone": "Vorlage klonen", @@ -1142,14 +1157,6 @@ "title": "{length} {length, plural, one{archiviertes Dossier} other{archivierte Dossiers}}" } }, - "dossier-templates": { - "label": "Dossier-Vorlagen", - "status": { - "active": "Aktiv", - "inactive": "Inaktiv", - "incomplete": "Unvollständig" - } - }, "dossier-watermark-selector": { "heading": "Wasserzeichen auf Dokumenten", "no-watermark": "Kein Wasserzeichen in der Dossier-Vorlage verfügbar:
Bitten Sie Ihren Admin, eines zu konfigurieren.", @@ -1317,7 +1324,7 @@ "options": { "in-document": { "description": "", - "label": "In Dokument ändern" + "label": "Im Dokument bearbeiten" }, "only-here": { "description": "", @@ -1360,6 +1367,15 @@ "title": "{length} {length, plural, one{Wörterbuch} other{Wörterbücher}}" } }, + "entity": { + "info": { + "actions": { + "revert": "Zurücksetzen", + "save": "Änderungen speichern" + }, + "heading": "Entität bearbeiten" + } + }, "entity-rules-screen": { "error": { "generic": "Fehler: Aktualisierung der Entitätsregeln fehlgeschlagen." @@ -1373,28 +1389,19 @@ "title": "Entitätsregeln-Editor", "warnings-found": "{warnings, plural, one{A warning} other{{warnings} warnings}} in Regeln gefunden" }, - "entity": { - "info": { - "actions": { - "revert": "Zurücksetzen", - "save": "Änderungen speichern" - }, - "heading": "Entität bearbeiten" - } - }, "error": { "deleted-entity": { "dossier": { "action": "Zurück zur Übersicht", "label": "Dieses Dossier wurde gelöscht." }, - "file-dossier": { - "action": "Zurück zur Übersicht", - "label": "Das Dossier dieser Datei wurde gelöscht." - }, "file": { "action": "Zurück zum Dossier", "label": "Diese Datei wurde gelöscht." + }, + "file-dossier": { + "action": "Zurück zur Übersicht", + "label": "Das Dossier dieser Datei wurde gelöscht." } }, "file-preview": { @@ -1412,6 +1419,12 @@ }, "exact-date": "{day} {month} {year} um {hour}:{minute} Uhr", "file": "Datei", + "file-attribute": { + "update": { + "error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.", + "success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert." + } + }, "file-attribute-encoding-types": { "ascii": "ASCII", "iso": "ISO-8859-1", @@ -1422,12 +1435,6 @@ "number": "Nummer", "text": "Freier Text" }, - "file-attribute": { - "update": { - "error": "Aktualisierung des Werts für das Datei-Attribut fehlgeschlagen. Bitte versuchen Sie es noch einmal.", - "success": "Der Wert für das Dateiattribut wurde erfolgreich aktualisiert." - } - }, "file-attributes-configurations": { "cancel": "Abbrechen", "form": { @@ -1642,18 +1649,9 @@ "file-upload": { "type": { "csv": "Die Datei-Attribute wurden erfolgreich aus der hochgeladenen CSV-Datei importiert.", - "zip": "" + "zip": "Die Zip-Datei wurde erfolgreich hochgeladen!" } }, - "filter-menu": { - "filter-options": "Filteroptionen", - "filter-types": "Filter", - "label": "Filter", - "pages-without-annotations": "Nur Seiten ohne Annotationen", - "redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen", - "unseen-pages": "Nur Anmerkungen auf ungesehenen Seiten", - "with-comments": "Nur Anmerkungen mit Kommentaren" - }, "filter": { "analysis": "Analyse erforderlich", "comment": "Kommentare", @@ -1663,6 +1661,15 @@ "redaction": "Annotationen", "updated": "Aktualisiert" }, + "filter-menu": { + "filter-options": "Filteroptionen", + "filter-types": "Filter", + "label": "Filter", + "pages-without-annotations": "Nur Seiten ohne Annotationen", + "redaction-changes": "Nur Annotationen mit lokalen manuellen Änderungen", + "unseen-pages": "Nur Anmerkungen auf ungesehenen Seiten", + "with-comments": "Nur Anmerkungen mit Kommentaren" + }, "filters": { "assigned-people": "Bearbeiter", "documents-status": "Dokumentenstatus", @@ -1731,11 +1738,11 @@ "title": "SMTP-Konto konfigurieren" }, "generic-errors": { - "400": "", - "403": "", - "404": "", - "409": "", - "500": "" + "400": "Die gesendete Anfrage ist ungültig.", + "403": "Der Zugriff auf die angeforderte Ressource ist nicht zulässig.", + "404": "Die angeforderte Ressource konnte nicht gefunden werden.", + "409": "Die Anfrage ist nicht mit dem aktuellen Status kompatibel.", + "500": "Der Server hat einen unerwarteten Fehler festgestellt und konnte die Anfrage nicht bearbeiten." }, "help-button": { "disable": "Hilfemodus deaktivieren", @@ -1907,15 +1914,15 @@ "legalBasis": "Rechtsgrundlage", "options": { "multiple-pages": { - "description": "Annotation auf folgenden Seiten bearbeiten", - "extraOptionDescription": "Minus (-) für Seitenbereich und Komma (,) für Aufzählung.", - "extraOptionLabel": "Seitenbereich", + "description": "Fügen Sie die Annotation auf mehreren Seiten hinzu.", + "extraOptionDescription": "Minus (-) für Bereich und Komma (,) für Aufzählung.", + "extraOptionLabel": "Seiten", "extraOptionPlaceholder": "z. B. 1-20,22,32", "label": "Auf mehreren Seiten anwenden" }, "only-this-page": { - "description": "Annotation nur an dieser Position im Dokument bearbeiten", - "label": "Auf dieser Seite anwenden" + "description": "Fügen Sie die Annotation nur an dieser Stelle im Dokument hinzu.", + "label": "Nur auf dieser Seite anwenden" } }, "reason": "Grund", @@ -1942,15 +1949,22 @@ "document-approved": "{fileHref, select, null{{fileName}} other{{fileName}}} wurde genehmigt.", "dossier-deleted": "Dossier wurde gelöscht: {dossierName}", "dossier-owner-deleted": "Der Besitzer des Dossiers wurde gelöscht: {dossierName}", - "dossier-owner-removed": "Der Dossier-Owner von {dossierHref, select, null{{dossierName}} other{{dossierName}}} wurde entfernt!", + "dossier-owner-removed": "Sie wurden {dossierHref, select, null{{dossierName}} other{{dossierName}}} ", "dossier-owner-set": "Sie sind jetzt Besitzer des Dossiers {dossierHref, select, null{{dossierName}} other{{dossierName}}}.", "download-ready": "Ihr Download steht bereit.", "no-data": "Sie haben aktuell keine Benachrichtigungen", "unassigned-from-file": "Sie wurden von einem Dokument entfernt.
Dokument: {fileHref, select, null{{fileName}} other{{fileName}}}
Dossier: {dossierHref, select, null{{dossierName}} other{{dossierHref, select, null{{dossierName}} other{{dossierName}}}}}", - "user-becomes-dossier-member": "{user} ist jetzt Mitglied des Dossiers {dossierHref, select, null{{dossierName}} other{{dossierName}}}!", + "user-becomes-dossier-member": "Sie wurden zu einem Dossier hinzugefügt: {dossierHref, select, null{{dossierName}} other{{dossierName}}}!", "user-demoted-to-reviewer": "Sie wurden auf die Reviewer-Rolle heruntergestuft: {dossierHref, select, null{{dossierName}}\n other{{dossierName}}}", "user-promoted-to-approver": "Sie wurden in einem Dossier zum Genehmiger ernannt: {dossierHref, select, null{{dossierName}} other{{dossierName}}}", - "user-removed-as-dossier-member": "{user} wurde als Mitglied von: {dossierHref, select, null{{dossierName}} other{{dossierName}}} entfernt!" + "user-removed-as-dossier-member": "Sie wurden als Mitglied entfernt: {dossierHref, select, null{{dossierName}} other{{dossierName}}} " + }, + "notifications": { + "button-text": "Benachrichtigungen", + "deleted-dossier": "Gelöschtes Dossier", + "label": "Benachrichtigungen", + "mark-all-as-read": "Alle als gelesen markieren", + "mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren" }, "notifications-screen": { "category": { @@ -1958,14 +1972,13 @@ "in-app-notifications": "In-App-Benachrichtigungen" }, "error": { - "generic": "Fehler: Aktualisierung der Präferenzen fehlgeschlagen." + "generic": "Aktualisierung der Präferenzen fehlgeschlagen." }, "groups": { "document": "Benachrichtigungen zu Dokumenten", "dossier": "Benachrichtigungen zu Dossiers", "other": "Andere Benachrichtigungen" }, - "options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten", "options": { "ASSIGN_APPROVER": "Wenn ich einem Dokument als Genehmiger zugewiesen bin", "ASSIGN_REVIEWER": "Wenn ich einem Dokument als Prüfer zugewiesen werde", @@ -1983,6 +1996,7 @@ "USER_PROMOTED_TO_APPROVER": "Wenn ich Genehmiger in einem Dossier werde", "USER_REMOVED_AS_DOSSIER_MEMBER": "Wenn ich die Dossier-Mitgliedschaft verliere" }, + "options-title": "Wählen Sie aus, bei welchen Aktivitäten Sie benachrichtigt werden möchten", "schedule": { "daily": "Tägliche Zusammenfassung", "instant": "Sofortig", @@ -1990,13 +2004,6 @@ }, "title": "Benachrichtigungseinstellungen" }, - "notifications": { - "button-text": "Benachrichtigungen", - "deleted-dossier": "Gelöschtes Dossier", - "label": "Benachrichtigungen", - "mark-all-as-read": "Alle als gelesen markieren", - "mark-as": "Als {type, select, read{gelesen} unread{ungelesen} other{}} markieren" - }, "ocr": { "confirmation-dialog": { "cancel": "Abbrechen", @@ -2048,20 +2055,20 @@ "compare-button": "Vergleichen", "layers-panel-button": "Ebenen", "left-panel-button": "Panel", - "load-all-annotations": "Alle Annotationen geladen", + "load-all-annotations": "Alle Annotationen laden", "no-outlines-text": "Keine Gliederung verfügbar", "no-signatures-text": "Dieses Dokument enthält keine Unterschriftenfelder.", "outline-multi-select": "Bearbeiten", "outlines-panel-button": "Gliederung", "pan-tool-button": "Verschieben", - "rectangle-tool-button": "Bereich schwärzen", - "rotate-left-button": "Seite nach links drehen", - "rotate-right-button": "Seite nach rechts drehen", + "rectangle-tool-button": "Bereich annotieren", + "rotate-left-button": "Nach links drehen", + "rotate-right-button": "Nach rechts drehen", "select-tool-button": "Auswählen", "signature-panel-button": "Unterschriften", "thumbnails-panel-button": "Miniaturansicht", - "toggle-layers": "Layout-Raster {active, select, true{deaktivieren} false{aktivieren} other{}}", - "toggle-readable-redactions": "Schwärzungen {active, select, true{wie im finalen Dokument} false{in Preview-Farbe anzeigen} other{}}", + "toggle-layers": "Layoutraster {active, select, true{deaktivieren} false{aktivieren} other{}}", + "toggle-readable-redactions": "Show redactions {active, select, true{as in final document} false{in preview color} other{}}", "toggle-tooltips": "Tooltips für Annotationen {active, select, true{deaktivieren} false{aktivieren} other{}}", "zoom-in-button": "Vergrößern", "zoom-out-button": "Verkleinern" @@ -2108,6 +2115,10 @@ "warnings-label": "Dialoge und Meldungen", "warnings-subtitle": "„Nicht mehr anzeigen“-Optionen" }, + "processing": { + "basic": "Verarbeitung läuft", + "ocr": "OCR" + }, "processing-status": { "ocr": "OCR", "pending": "Ausstehend", @@ -2115,10 +2126,6 @@ "processed": "Verarbeitet", "processing": "Verarbeitung läuft" }, - "processing": { - "basic": "Verarbeitung läuft", - "ocr": "OCR" - }, "readonly": "Lesemodus", "readonly-archived": "Lesemodus (archiviert)", "redact-text": { @@ -2135,8 +2142,8 @@ "legal-basis": "Rechtsgrundlage", "options": { "in-document": { - "description": "Fügen Sie die Schwärzung an allen Stellen in diesem Dokument hinzu.", - "label": "Im Dokument schwärzen" + "description": "Add redaction for each occurrence of the term in this document.", + "label": "Redact in document" }, "in-dossier": { "description": "Fügen Sie die Schwärzung zu jedem Dokument in {dossierName} hinzu.", @@ -2154,7 +2161,7 @@ "type": "Typ", "type-placeholder": "Typ auswählen...", "unchanged": "", - "value": "Wert" + "value": "Value" }, "title": "Text schwärzen" } @@ -2368,6 +2375,16 @@ "header": "Größe von {type} ändern" } }, + "revert-manual-changes-dialog": { + "actions": { + "cancel": "Abbrechen", + "save": "Zu den Originalen zurückkehren" + }, + "details": { + "title": "Manuelle Änderungen" + }, + "title": "Lokale manuelle Änderungen rückgängig machen" + }, "revert-value-dialog": { "actions": { "cancel": "Abbrechen", @@ -2388,6 +2405,12 @@ "red-user-admin": "Benutzeradmin", "regular": "regulärer Benutzer" }, + "search": { + "active-dossiers": "Dokumente in aktiven Dossiers", + "all-dossiers": "Alle Dokumente", + "placeholder": "Dokumente durchsuchen...", + "this-dossier": "In diesem Dossier" + }, "search-screen": { "cols": { "assignee": "Bearbeiter", @@ -2411,12 +2434,6 @@ "no-match": "Der Suchbegriff wurde in keinem der Dokumente gefunden.", "table-header": "{length} {length, plural, one{Suchergebnis} other{Suchergebnisse}}" }, - "search": { - "active-dossiers": "Dokumente in aktiven Dossiers", - "all-dossiers": "Alle Dokumente", - "placeholder": "Dokumente durchsuchen...", - "this-dossier": "In diesem Dossier" - }, "seconds": "Sekunden", "size": "Größe", "smtp-auth-config": { @@ -2521,7 +2538,7 @@ "overwrite": "Überschreiben" }, "question": "Wie möchten Sie vorgehen?", - "title": "Das Wörterbuch hat bereits Einträge!" + "title": "Das Wörterbuch hat bereits Einträge." }, "upload-file": { "upload-area-text": "Klicken Sie hier oder ziehen Sie die Datei in diesen Bereich..." diff --git a/apps/red-ui/src/assets/i18n/scm/en.json b/apps/red-ui/src/assets/i18n/scm/en.json index 93aafcfb6..86bafda91 100644 --- a/apps/red-ui/src/assets/i18n/scm/en.json +++ b/apps/red-ui/src/assets/i18n/scm/en.json @@ -338,6 +338,10 @@ "error": "Failed to remove annotation: {error}", "success": "Annotation removed" }, + "revert-changes": { + "error": "Failed to revert manual changes: {error}", + "success": "Reverted manual changes successfully." + }, "undo": { "error": "Failed to undo: {error}", "success": "Undo successful" @@ -359,6 +363,9 @@ "resize": { "label": "Resize" }, + "revert-changes": { + "label": "Revert changes" + }, "see-references": { "label": "See references" }, @@ -1317,7 +1324,7 @@ "options": { "in-document": { "description": "", - "label": "In Dokument ändern" + "label": "Edit in document" }, "only-here": { "description": "", @@ -1907,9 +1914,9 @@ "legalBasis": "Legal Basis", "options": { "multiple-pages": { - "description": "Edit annotation the range of pages", + "description": "Add annotation on a range of pages", "extraOptionDescription": "Minus(-) for range and comma(,) for enumeration", - "extraOptionLabel": "Range", + "extraOptionLabel": "Pages", "extraOptionPlaceholder": "e.g. 1-20,22,32", "label": "Apply on multiple pages" }, @@ -1958,7 +1965,7 @@ "in-app-notifications": "In-app notifications" }, "error": { - "generic": "Something went wrong... Preferences update failed." + "generic": "Preferences update failed." }, "groups": { "document": "Document related notifications", @@ -2368,6 +2375,16 @@ "header": "Resize {type}" } }, + "revert-manual-changes-dialog": { + "actions": { + "cancel": "Cancel", + "save": "Revert to originals" + }, + "details": { + "title": "Manual changes" + }, + "title": "Revert local manual changes" + }, "revert-value-dialog": { "actions": { "cancel": "Cancel", diff --git a/apps/red-ui/src/assets/icons/general/revert-changes.svg b/apps/red-ui/src/assets/icons/general/revert-changes.svg new file mode 100644 index 000000000..a4596ff66 --- /dev/null +++ b/apps/red-ui/src/assets/icons/general/revert-changes.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/libs/common-ui b/libs/common-ui index a4e3ed885..aa5fc5457 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit a4e3ed8854604fccd87579a3f3b8a77dc7b9c1ca +Subproject commit aa5fc5457618304ae413956cc8239cd79b660862 diff --git a/libs/red-domain/.eslintrc.json b/libs/red-domain/.eslintrc.json index 318fd729f..2c3ac489a 100644 --- a/libs/red-domain/.eslintrc.json +++ b/libs/red-domain/.eslintrc.json @@ -22,6 +22,7 @@ "style": "kebab-case" } ], + "@angular-eslint/no-host-metadata-property": "off", "@typescript-eslint/unbound-method": "error", "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/naming-convention": [ diff --git a/libs/red-domain/src/lib/annotations/types.ts b/libs/red-domain/src/lib/annotations/types.ts index 97355218a..f56478dff 100644 --- a/libs/red-domain/src/lib/annotations/types.ts +++ b/libs/red-domain/src/lib/annotations/types.ts @@ -10,7 +10,8 @@ export type ManualRedactionActions = | 'undo' | 'force-redaction' | 'force-hint' - | 'recategorize-annotation'; + | 'recategorize-annotation' + | 'revert-changes'; export const AnnotationIconTypes = { square: 'square',