Merge master into RED-7497
This commit is contained in:
commit
c0ff902f69
@ -1,4 +1,4 @@
|
||||
<iqser-help-mode *ngIf="!config.IS_DOCUMINE"></iqser-help-mode>
|
||||
<iqser-help-mode></iqser-help-mode>
|
||||
|
||||
<div class="top-bar">
|
||||
<ng-template #menuPlaceholder>
|
||||
@ -21,11 +21,11 @@
|
||||
<redaction-spotlight-search
|
||||
*allow="roles.search; if: (isSearchScreen$ | async) === false && (currentUser.isUser || currentUser.isManager)"
|
||||
[actions]="searchActions"
|
||||
[attr.help-mode-key]="'search_in_entire_application'"
|
||||
[attr.help-mode-key]="'search_entire_application'"
|
||||
[placeholder]="'search.placeholder' | translate"
|
||||
></redaction-spotlight-search>
|
||||
|
||||
<iqser-help-button *ngIf="!config.IS_DOCUMINE" [attr.help-mode-key]="'help_mode'" id="help-mode-button"></iqser-help-button>
|
||||
<iqser-help-button [attr.help-mode-key]="'help_mode'" id="help-mode-button"></iqser-help-button>
|
||||
|
||||
<redaction-notifications
|
||||
*ngIf="currentUser.isUser || currentUser.isManager"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div *ngIf="breadcrumbsService.breadcrumbs$ | async as breadcrumbs" [attr.help-mode-key]="'navigate_in_breadcrumbs'" class="breadcrumbs">
|
||||
<div *ngIf="breadcrumbsService.breadcrumbs$ | async as breadcrumbs" [attr.help-mode-key]="'navigate_breadcrumbs'" class="breadcrumbs">
|
||||
<a *ngIf="breadcrumbs.length === 0; else items" class="breadcrumb back" redactionNavigateLastDossiersScreen>
|
||||
<mat-icon svgIcon="iqser:expand"></mat-icon>
|
||||
{{ 'top-bar.navigation-items.back' | translate }}
|
||||
@ -29,15 +29,17 @@
|
||||
></iqser-chevron-button>
|
||||
|
||||
<mat-menu #dropdownMenu="matMenu" class="padding-bottom-8">
|
||||
<a
|
||||
*ngFor="let option of breadcrumb.options.options"
|
||||
[routerLink]="option.options.routerLink | tenant"
|
||||
mat-menu-item
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ option.name$ | async }}
|
||||
<mat-icon class="checkmark" svgIcon="iqser:check"></mat-icon>
|
||||
</a>
|
||||
<div id="breadcrumbs-menu-items">
|
||||
<a
|
||||
*ngFor="let option of breadcrumb.options.options"
|
||||
[routerLink]="option.options.routerLink | tenant"
|
||||
mat-menu-item
|
||||
routerLinkActive="active"
|
||||
>
|
||||
{{ option.name$ | async }}
|
||||
<mat-icon class="checkmark" svgIcon="iqser:check"></mat-icon>
|
||||
</a>
|
||||
</div>
|
||||
</mat-menu>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
@ -25,9 +25,8 @@ export const canRemoveFromDictionary = (annotation: AnnotationWrapper) =>
|
||||
!annotation.pending &&
|
||||
!annotation.hasBeenResized;
|
||||
|
||||
export const canRemoveRedaction = (annotations: AnnotationWrapper[], permissions: AnnotationPermissions) =>
|
||||
annotations.length === 1 &&
|
||||
(permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive);
|
||||
export const canRemoveRedaction = (permissions: AnnotationPermissions) =>
|
||||
permissions.canRemoveOnlyHere || permissions.canRemoveFromDictionary || permissions.canMarkAsFalsePositive;
|
||||
|
||||
export const canChangeLegalBasis = (annotation: AnnotationWrapper, canAddRedaction: boolean) =>
|
||||
canAddRedaction && annotation.isRedacted && !annotation.pending;
|
||||
|
||||
@ -29,6 +29,7 @@ export class AnnotationPermissions {
|
||||
canResizeAnnotation = true;
|
||||
canRecategorizeAnnotation = true;
|
||||
canForceHint = true;
|
||||
canEditAnnotations = true;
|
||||
|
||||
static forUser(
|
||||
isApprover: boolean,
|
||||
@ -46,7 +47,6 @@ export class AnnotationPermissions {
|
||||
for (const annotation of annotations) {
|
||||
const permissions: AnnotationPermissions = new AnnotationPermissions();
|
||||
const annotationEntity = entities.find(entity => entity.type === annotation.type);
|
||||
|
||||
permissions.canUndo = canUndo(annotation, isApprover);
|
||||
permissions.canForceHint = canForceHint(annotation, canAddRedaction);
|
||||
permissions.canForceRedaction = canForceRedaction(annotation, canAddRedaction);
|
||||
@ -54,10 +54,11 @@ export class AnnotationPermissions {
|
||||
permissions.canMarkAsFalsePositive = canMarkAsFalsePositive(annotation, annotationEntity);
|
||||
permissions.canRemoveOnlyHere = canRemoveOnlyHere(annotation, canAddRedaction);
|
||||
permissions.canRemoveFromDictionary = canRemoveFromDictionary(annotation);
|
||||
permissions.canRemoveRedaction = canRemoveRedaction(annotations, permissions);
|
||||
permissions.canRemoveRedaction = canRemoveRedaction(permissions);
|
||||
permissions.canChangeLegalBasis = canChangeLegalBasis(annotation, canAddRedaction);
|
||||
permissions.canRecategorizeAnnotation = canRecategorizeAnnotation(annotation, canAddRedaction);
|
||||
permissions.canResizeAnnotation = canResizeAnnotation(annotation, canAddRedaction);
|
||||
permissions.canEditAnnotations = annotation.isSkipped || annotation.isRedacted;
|
||||
|
||||
summedPermissions._merge(permissions);
|
||||
}
|
||||
@ -77,6 +78,7 @@ export class AnnotationPermissions {
|
||||
result.canRemoveOnlyHere = permissions.reduce((acc, next) => acc && next.canRemoveOnlyHere, true);
|
||||
result.canRemoveRedaction = permissions.reduce((acc, next) => acc && next.canRemoveRedaction, true);
|
||||
result.canUndo = permissions.reduce((acc, next) => acc && next.canUndo, true);
|
||||
result.canEditAnnotations = permissions.reduce((acc, next) => acc && next.canEditAnnotations, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -302,6 +302,7 @@ export class AnnotationWrapper implements IListable {
|
||||
changeLogType: ChangeType,
|
||||
legalBasisList: ILegalBasis[],
|
||||
hintDictionary: boolean,
|
||||
isDocumine: boolean,
|
||||
) {
|
||||
const annotationWrapper = new AnnotationWrapper();
|
||||
|
||||
@ -351,7 +352,7 @@ export class AnnotationWrapper implements IListable {
|
||||
c => c.manualRedactionType === ManualRedactionTypes.LEGAL_BASIS_CHANGE && c.annotationStatus === LogEntryStatuses.REQUESTED,
|
||||
)?.propertyChanges.legalBasis;
|
||||
|
||||
this.#createContent(annotationWrapper, redactionLogEntry);
|
||||
this.#createContent(annotationWrapper, redactionLogEntry, isDocumine);
|
||||
this.#setSuperType(annotationWrapper, redactionLogEntry);
|
||||
this.#handleRecommendations(annotationWrapper, redactionLogEntry);
|
||||
annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);
|
||||
@ -421,20 +422,24 @@ export class AnnotationWrapper implements IListable {
|
||||
}
|
||||
}
|
||||
|
||||
static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry) {
|
||||
static #createContent(annotationWrapper: AnnotationWrapper, entry: IRedactionLogEntry, isDocumine: boolean) {
|
||||
let content = '';
|
||||
if (entry.matchedRule) {
|
||||
content += `Rule ${entry.matchedRule} matched \n\n`;
|
||||
content += `Rule ${entry.matchedRule} matched${isDocumine ? ':' : ''} \n\n`;
|
||||
}
|
||||
|
||||
if (entry.reason) {
|
||||
if (isDocumine && entry.reason.slice(-1) === '.') {
|
||||
entry.reason = entry.reason.slice(0, -1);
|
||||
}
|
||||
|
||||
content += entry.reason + '\n\n';
|
||||
//remove leading and trailing commas and whitespaces
|
||||
content = content.replace(/(^[, ]*)|([, ]*$)/g, '');
|
||||
content = content.substring(0, 1).toUpperCase() + content.substring(1);
|
||||
}
|
||||
|
||||
if (annotationWrapper.legalBasis) {
|
||||
if (annotationWrapper.legalBasis && !isDocumine) {
|
||||
content += 'Legal basis: ' + annotationWrapper.legalBasis + '\n\n';
|
||||
}
|
||||
|
||||
@ -443,7 +448,7 @@ export class AnnotationWrapper implements IListable {
|
||||
}
|
||||
|
||||
if (entry.section) {
|
||||
let prefix = 'In section: ';
|
||||
let prefix = `In section${isDocumine ? '' : ':'} `;
|
||||
if (content.length) {
|
||||
prefix = ` ${prefix.toLowerCase()}`;
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*ngIf="item.show"
|
||||
[routerLinkActiveOptions]="{ exact: false }"
|
||||
[routerLink]="'../' + item.screen"
|
||||
[attr.help-mode-key]="'user_account'"
|
||||
[attr.help-mode-key]="item.helpModeKey"
|
||||
class="item"
|
||||
routerLinkActive="active"
|
||||
>
|
||||
|
||||
@ -9,6 +9,7 @@ interface NavItem {
|
||||
readonly label: string;
|
||||
readonly screen: string;
|
||||
readonly show?: boolean;
|
||||
readonly helpModeKey?: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -24,21 +25,25 @@ export class AccountSideNavComponent {
|
||||
screen: 'user-profile',
|
||||
label: _('user-profile'),
|
||||
show: true,
|
||||
helpModeKey: 'my_profile',
|
||||
},
|
||||
{
|
||||
screen: 'notifications',
|
||||
show: this.currentUser.isUser && this._permissionsService.has(Roles.notifications.write),
|
||||
label: _('notifications.label'),
|
||||
helpModeKey: 'notification_preferences',
|
||||
},
|
||||
{
|
||||
screen: 'preferences',
|
||||
label: _('preferences-screen.label'),
|
||||
show: this.currentUser.isUser,
|
||||
helpModeKey: 'user_preferences',
|
||||
},
|
||||
{
|
||||
screen: 'warnings-preferences',
|
||||
label: _('preferences-screen.warnings-label'),
|
||||
show: this.currentUser.isUser,
|
||||
helpModeKey: 'prompts_and_dialogs',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { NotificationPreferencesService } from '../../../services/notification-p
|
||||
import { BaseFormComponent, getConfig, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import {
|
||||
DocumentNotificationsTypes,
|
||||
NotificationCategoriesValues,
|
||||
NotificationGroupsKeys,
|
||||
NotificationGroupsValues,
|
||||
@ -77,7 +78,8 @@ export class NotificationsScreenComponent extends BaseFormComponent implements O
|
||||
async save() {
|
||||
this.#loadingService.start();
|
||||
try {
|
||||
await firstValueFrom(this.#notificationPreferencesService.update(this.form.value));
|
||||
const preferences = this.#filterNotificationPreferences();
|
||||
await firstValueFrom(this.#notificationPreferencesService.update(preferences));
|
||||
} catch (e) {
|
||||
this.#toaster.error(_('notifications-screen.error.generic'));
|
||||
}
|
||||
@ -106,4 +108,27 @@ export class NotificationsScreenComponent extends BaseFormComponent implements O
|
||||
|
||||
this.#loadingService.stop();
|
||||
}
|
||||
|
||||
#filterNotificationPreferences() {
|
||||
if (!this.#config.IS_DOCUMINE) {
|
||||
return this.form.value;
|
||||
}
|
||||
|
||||
if (!this.isPreferenceChecked('inAppNotifications', DocumentNotificationsTypes.assignApprover)) {
|
||||
return {
|
||||
...this.form.value,
|
||||
inAppNotifications: this.form
|
||||
.get('inAppNotifications')
|
||||
.value.filter((preference: string) => !RSS_EXCLUDED_SETTINGS.includes(preference)),
|
||||
};
|
||||
}
|
||||
if (!this.form.get('inAppNotifications').value.includes(DocumentNotificationsTypes.assignReviewer)) {
|
||||
return {
|
||||
...this.form.value,
|
||||
inAppNotifications: [...this.form.get('inAppNotifications').value, DocumentNotificationsTypes.assignReviewer],
|
||||
};
|
||||
}
|
||||
|
||||
return this.form.value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +61,19 @@ const dossierTemplateIdRoutes: IqserRoutes = [
|
||||
},
|
||||
loadChildren: () => import('./screens/rules/rules.module').then(m => m.RulesModule),
|
||||
},
|
||||
{
|
||||
path: 'component-rules',
|
||||
component: BaseDossierTemplateScreenComponent,
|
||||
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
|
||||
data: {
|
||||
routeGuards: [IqserAuthGuard, RedRoleGuard],
|
||||
permissions: {
|
||||
allow: [Roles.rules.read],
|
||||
redirectTo: 'info',
|
||||
},
|
||||
},
|
||||
loadChildren: () => import('./screens/component-rules/component-rules.module').then(m => m.ComponentRulesModule),
|
||||
},
|
||||
{
|
||||
path: 'file-attributes',
|
||||
component: BaseDossierTemplateScreenComponent,
|
||||
|
||||
@ -57,6 +57,7 @@ import { DossierTemplateActionsComponent } from './shared/components/dossier-tem
|
||||
import { IqserUsersModule } from '@iqser/common-ui/lib/users';
|
||||
import { TenantPipe } from '@iqser/common-ui/lib/tenants';
|
||||
import { SelectComponent } from '@shared/components/select/select.component';
|
||||
import { ComponentRulesService } from './services/component-rules.service';
|
||||
|
||||
const dialogs = [
|
||||
AddEditCloneDossierTemplateDialogComponent,
|
||||
@ -97,7 +98,7 @@ const components = [
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, SmtpConfigService],
|
||||
providers: [AdminDialogService, AuditService, DigitalSignatureService, RulesService, ComponentRulesService, SmtpConfigService],
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ComponentRulesScreenComponent } from './rules-screen/component-rules-screen.component';
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
||||
import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IconButtonComponent } from '@iqser/common-ui';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
const routes = [{ path: '', component: ComponentRulesScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||
|
||||
@NgModule({
|
||||
declarations: [ComponentRulesScreenComponent],
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
MonacoEditorModule,
|
||||
TranslateModule,
|
||||
IconButtonComponent,
|
||||
FormsModule,
|
||||
MatIconModule,
|
||||
],
|
||||
})
|
||||
export class ComponentRulesModule {}
|
||||
@ -0,0 +1,26 @@
|
||||
<div class="header-container">
|
||||
<div [translate]="'component-rules-screen.title'" class="heading-l"></div>
|
||||
<div [translate]="'component-rules-screen.warning-text'" class="error"></div>
|
||||
</div>
|
||||
|
||||
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
|
||||
|
||||
<div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box">
|
||||
<div (click)="goToErrors()" *ngIf="numberOfErrors()" class="errors">
|
||||
<span>
|
||||
<mat-icon [svgIcon]="'iqser:alert-circle'" class="icon"></mat-icon>
|
||||
<span [translateParams]="{ errors: numberOfErrors() }" [translate]="'component-rules-screen.errors-found'"></span>
|
||||
</span>
|
||||
<mat-icon [svgIcon]="'iqser:expand'" class="icon face-up"></mat-icon>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<iqser-icon-button
|
||||
(action)="save()"
|
||||
[label]="'component-rules-screen.save-changes' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
icon="iqser:check"
|
||||
></iqser-icon-button>
|
||||
|
||||
<div (click)="revert()" [translate]="'component-rules-screen.revert-changes'" class="all-caps-label cancel"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,67 @@
|
||||
:host {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
padding: 15px 0 0 15px;
|
||||
}
|
||||
|
||||
ngx-monaco-editor {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-bottom: 15px;
|
||||
margin-left: 30px;
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
font-size: 8px;
|
||||
font-weight: 500;
|
||||
margin-left: 20px;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.changes-box {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
gap: 10px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.errors {
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
scale: 0.7;
|
||||
}
|
||||
|
||||
:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
font-weight: 500;
|
||||
/* TODO: Update this to --iqser-error when added */
|
||||
color: #dd4d50;
|
||||
}
|
||||
|
||||
.face-up {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,216 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, ElementRef, OnInit, signal, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { saveAs } from 'file-saver';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { DOSSIER_TEMPLATE_ID } from '@red/domain';
|
||||
import { EditorThemeService } from '@services/editor-theme.service';
|
||||
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
|
||||
import { Debounce, getParam } from '@iqser/common-ui/lib/utils';
|
||||
import { ComponentRulesService } from '../../../services/component-rules.service';
|
||||
import ICodeEditor = monaco.editor.ICodeEditor;
|
||||
import IModelDeltaDecoration = monaco.editor.IModelDeltaDecoration;
|
||||
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
|
||||
|
||||
interface SyntaxError {
|
||||
line: number;
|
||||
column: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
templateUrl: './component-rules-screen.component.html',
|
||||
styleUrls: ['./component-rules-screen.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ComponentRulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||
theme: 'vs',
|
||||
language: 'java',
|
||||
automaticLayout: true,
|
||||
readOnly: !this.permissionsService.canEditRules(),
|
||||
glyphMargin: true,
|
||||
};
|
||||
initialLines: string[] = [];
|
||||
currentLines: string[] = [];
|
||||
isLeaving = false;
|
||||
@ViewChild('fileInput')
|
||||
private _fileInput: ElementRef;
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
readonly #errorGlyphs = signal<string[]>([]);
|
||||
readonly numberOfErrors = computed(() => this.#errorGlyphs().length);
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
private readonly _componentRulesService: ComponentRulesService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _editorThemeService: EditorThemeService,
|
||||
) {}
|
||||
|
||||
set isLeavingPage(isLeaving: boolean) {
|
||||
this.isLeaving = isLeaving;
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
return this.currentLines.toString() !== this.initialLines.toString();
|
||||
}
|
||||
|
||||
get codeEditorText() {
|
||||
return this.currentLines.join('\n');
|
||||
}
|
||||
|
||||
set codeEditorText($event: any) {
|
||||
this.currentLines = $event.split('\n');
|
||||
this.codeEditorTextChanged();
|
||||
this._closeProblemsView();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
await this._initialize();
|
||||
}
|
||||
|
||||
onCodeEditorInit(editor: ICodeEditor) {
|
||||
this._codeEditor = editor;
|
||||
for (const theme of this._editorThemeService.themes) {
|
||||
(window as any).monaco.editor.defineTheme(theme, this._editorThemeService.configurations[theme]);
|
||||
}
|
||||
(window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(true));
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
@Debounce()
|
||||
codeEditorTextChanged() {
|
||||
const newDecorations = this.currentLines.filter(entry => this._isNew(entry)).map(entry => this._makeDecorationFor(entry));
|
||||
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
|
||||
}
|
||||
|
||||
goToErrors() {
|
||||
this._codeEditor.trigger(null, 'editor.action.marker.next', null);
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
this._removeErrorMarkers();
|
||||
await firstValueFrom(
|
||||
this._componentRulesService.uploadRules({
|
||||
rules: this._codeEditor.getModel().getValue(),
|
||||
dossierTemplateId: this.#dossierTemplateId,
|
||||
}),
|
||||
).then(
|
||||
async () => {
|
||||
await this._initialize();
|
||||
this._toaster.success(_('component-rules-screen.success.generic'));
|
||||
},
|
||||
error => {
|
||||
const errors = error.error as SyntaxError[] | undefined;
|
||||
this._drawErrorMarkers(errors);
|
||||
this._loadingService.stop();
|
||||
this._toaster.error(_('component-rules-screen.error.generic'));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
revert(): void {
|
||||
this.currentLines = this.initialLines;
|
||||
this._decorations = this._codeEditor?.deltaDecorations(this._decorations, []) || [];
|
||||
this._removeErrorMarkers();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
download(): void {
|
||||
const content = this._codeEditor.getModel().getValue();
|
||||
const blob = new Blob([content], {
|
||||
type: 'text/plain;charset=utf-8',
|
||||
});
|
||||
saveAs(blob, 'rules.txt');
|
||||
}
|
||||
|
||||
upload($event): void {
|
||||
const file: File = $event.target.files[0];
|
||||
const fileReader = new FileReader();
|
||||
|
||||
if (file) {
|
||||
fileReader.onload = () => {
|
||||
this._codeEditor.getModel().setValue(fileReader.result as string);
|
||||
this._fileInput.nativeElement.value = null;
|
||||
};
|
||||
fileReader.readAsText(file);
|
||||
}
|
||||
}
|
||||
|
||||
private _isNew(entry: string): boolean {
|
||||
return this.initialLines.indexOf(entry) < 0 && entry?.trim().length > 0;
|
||||
}
|
||||
|
||||
private _makeDecorationFor(entry: string): IModelDeltaDecoration {
|
||||
const line = this.currentLines.indexOf(entry) + 1;
|
||||
|
||||
return {
|
||||
range: new monaco.Range(line, 1, line, 1),
|
||||
options: { isWholeLine: true, className: 'changed-row-marker' },
|
||||
} as IModelDeltaDecoration;
|
||||
}
|
||||
|
||||
private _drawErrorMarkers(errors: SyntaxError[] | undefined) {
|
||||
const model = this._codeEditor?.getModel();
|
||||
if (!model || !errors?.length) {
|
||||
return;
|
||||
}
|
||||
const markers = [];
|
||||
const glyphs = [];
|
||||
errors
|
||||
.filter(e => e.line > 0)
|
||||
.forEach(e => {
|
||||
const endColumn = model.getLineLength(e.line) + 1;
|
||||
markers.push({
|
||||
message: e.message,
|
||||
severity: monaco.MarkerSeverity.Error,
|
||||
startLineNumber: e.line,
|
||||
startColumn: e.column,
|
||||
endLineNumber: e.line,
|
||||
endColumn,
|
||||
});
|
||||
glyphs.push({
|
||||
range: new monaco.Range(e.line, e.column, e.line, endColumn),
|
||||
options: {
|
||||
glyphMarginClassName: 'error-glyph-margin',
|
||||
},
|
||||
});
|
||||
});
|
||||
this.#errorGlyphs.set(this._codeEditor.deltaDecorations(this.#errorGlyphs(), glyphs));
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, markers);
|
||||
}
|
||||
|
||||
private _closeProblemsView() {
|
||||
this._codeEditor.trigger(null, 'closeMarkersNavigation', null);
|
||||
}
|
||||
|
||||
private _removeErrorMarkers() {
|
||||
const model = this._codeEditor?.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, []);
|
||||
this.#errorGlyphs.set(this._codeEditor.deltaDecorations(this.#errorGlyphs(), []) || []);
|
||||
this._closeProblemsView();
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
this._loadingService.start();
|
||||
await firstValueFrom(this._componentRulesService.download(this.#dossierTemplateId)).then(
|
||||
rules => {
|
||||
this.currentLines = this.initialLines = rules.rules.split('\n');
|
||||
this.revert();
|
||||
},
|
||||
() => this._loadingService.stop(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -6,12 +6,21 @@
|
||||
<ngx-monaco-editor (init)="onCodeEditorInit($event)" [(ngModel)]="codeEditorText" [options]="editorOptions"></ngx-monaco-editor>
|
||||
|
||||
<div *ngIf="changed && permissionsService.canEditRules() && !isLeaving" class="changes-box">
|
||||
<iqser-icon-button
|
||||
(action)="save()"
|
||||
[label]="'rules-screen.save-changes' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
icon="iqser:check"
|
||||
></iqser-icon-button>
|
||||
<div (click)="goToErrors()" *ngIf="numberOfErrors()" class="errors">
|
||||
<span>
|
||||
<mat-icon [svgIcon]="'iqser:alert-circle'" class="icon"></mat-icon>
|
||||
<span [translateParams]="{ errors: numberOfErrors() }" [translate]="'rules-screen.errors-found'"></span>
|
||||
</span>
|
||||
<mat-icon [svgIcon]="'iqser:expand'" class="icon face-up"></mat-icon>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<iqser-icon-button
|
||||
(action)="save()"
|
||||
[label]="'rules-screen.save-changes' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
icon="iqser:check"
|
||||
></iqser-icon-button>
|
||||
|
||||
<div (click)="revert()" [translate]="'rules-screen.revert-changes'" class="all-caps-label cancel"></div>
|
||||
<div (click)="revert()" [translate]="'rules-screen.revert-changes'" class="all-caps-label cancel"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,3 +23,45 @@ ngx-monaco-editor {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.changes-box {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: start;
|
||||
gap: 10px;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.errors {
|
||||
cursor: pointer;
|
||||
|
||||
.icon {
|
||||
scale: 0.7;
|
||||
}
|
||||
|
||||
:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
font-weight: 500;
|
||||
/* TODO: Update this to --iqser-error when added */
|
||||
color: #dd4d50;
|
||||
}
|
||||
|
||||
.face-up {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.actions {
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, computed, ElementRef, OnInit, signal, ViewChild } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { saveAs } from 'file-saver';
|
||||
@ -40,7 +40,8 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
private _fileInput: ElementRef;
|
||||
private _codeEditor: ICodeEditor;
|
||||
private _decorations: string[] = [];
|
||||
private _errorGlyphs: string[] = [];
|
||||
readonly #errorGlyphs = signal<string[]>([]);
|
||||
readonly numberOfErrors = computed(() => this.#errorGlyphs().length);
|
||||
readonly #dossierTemplateId = getParam(DOSSIER_TEMPLATE_ID);
|
||||
|
||||
constructor(
|
||||
@ -68,6 +69,7 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
set codeEditorText($event: any) {
|
||||
this.currentLines = $event.split('\n');
|
||||
this.codeEditorTextChanged();
|
||||
this._closeProblemsView();
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -86,12 +88,16 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
@Debounce()
|
||||
codeEditorTextChanged() {
|
||||
const newDecorations = this.currentLines.filter(entry => this._isNew(entry)).map(entry => this._makeDecorationFor(entry));
|
||||
|
||||
this._decorations = this._codeEditor.deltaDecorations(this._decorations, newDecorations);
|
||||
}
|
||||
|
||||
goToErrors() {
|
||||
this._codeEditor.trigger(null, 'editor.action.marker.next', null);
|
||||
}
|
||||
|
||||
async save(): Promise<void> {
|
||||
this._loadingService.start();
|
||||
this._removeErrorMarkers();
|
||||
await firstValueFrom(
|
||||
this._rulesService.uploadRules({
|
||||
rules: this._codeEditor.getModel().getValue(),
|
||||
@ -100,7 +106,6 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
).then(
|
||||
async () => {
|
||||
await this._initialize();
|
||||
this._removeErrorMarkers();
|
||||
this._toaster.success(_('rules-screen.success.generic'));
|
||||
},
|
||||
error => {
|
||||
@ -180,17 +185,22 @@ export class RulesScreenComponent implements OnInit, ComponentCanDeactivate {
|
||||
},
|
||||
});
|
||||
});
|
||||
this._errorGlyphs = this._codeEditor.deltaDecorations(this._errorGlyphs, glyphs);
|
||||
this.#errorGlyphs.set(this._codeEditor.deltaDecorations(this.#errorGlyphs(), glyphs));
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, markers);
|
||||
}
|
||||
|
||||
private _closeProblemsView() {
|
||||
this._codeEditor.trigger(null, 'closeMarkersNavigation', null);
|
||||
}
|
||||
|
||||
private _removeErrorMarkers() {
|
||||
const model = this._codeEditor?.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
(window as any).monaco.editor.setModelMarkers(model, model.id, []);
|
||||
this._errorGlyphs = this._codeEditor?.deltaDecorations(this._errorGlyphs, []) || [];
|
||||
this.#errorGlyphs.set(this._codeEditor.deltaDecorations(this.#errorGlyphs(), []) || []);
|
||||
this._closeProblemsView();
|
||||
}
|
||||
|
||||
private async _initialize() {
|
||||
|
||||
@ -7,11 +7,20 @@ import { PendingChangesGuard } from '@guards/can-deactivate.guard';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IconButtonComponent } from '@iqser/common-ui';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
|
||||
const routes = [{ path: '', component: RulesScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||
|
||||
@NgModule({
|
||||
declarations: [RulesScreenComponent],
|
||||
imports: [RouterModule.forChild(routes), CommonModule, MonacoEditorModule, TranslateModule, IconButtonComponent, FormsModule],
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
MonacoEditorModule,
|
||||
TranslateModule,
|
||||
IconButtonComponent,
|
||||
FormsModule,
|
||||
MatIconModule,
|
||||
],
|
||||
})
|
||||
export class RulesModule {}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { GenericService } from '@iqser/common-ui';
|
||||
import { IComponentRules } from '@red/domain';
|
||||
|
||||
@Injectable()
|
||||
export class ComponentRulesService extends GenericService<IComponentRules> {
|
||||
protected readonly _defaultModelPath = 'rules';
|
||||
|
||||
download(dossierTemplateId: string) {
|
||||
return this._getOne([dossierTemplateId]);
|
||||
}
|
||||
|
||||
uploadRules(body: IComponentRules) {
|
||||
return this._post<unknown>(body);
|
||||
}
|
||||
}
|
||||
@ -33,10 +33,9 @@ export class AdminSideNavComponent implements OnInit {
|
||||
readonly translations = adminSideNavTranslations;
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
readonly roles = Roles;
|
||||
prefix: string;
|
||||
readonly isDocumine = this.#config.IS_DOCUMINE;
|
||||
readonly canAccessRulesInDocumine = this.isDocumine && !this.#config.RULE_EDITOR_DEV_ONLY;
|
||||
prefix: string;
|
||||
|
||||
readonly items: { readonly [key in AdminSideNavType]: NavItem[] } = {
|
||||
settings: [
|
||||
{
|
||||
@ -86,13 +85,13 @@ export class AdminSideNavComponent implements OnInit {
|
||||
{
|
||||
screen: 'info',
|
||||
label: _('admin-side-nav.dossier-template-info'),
|
||||
helpModeKey: 'dossier_templates_info',
|
||||
helpModeKey: this.currentUser.isAdmin ? 'dossier_templates_info' : 'user_dossier_template_info',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
screen: 'entities',
|
||||
label: _('admin-side-nav.entities'),
|
||||
helpModeKey: 'entities',
|
||||
helpModeKey: this.currentUser.isAdmin ? 'entities' : 'user_dossier_template_entities',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
@ -100,6 +99,14 @@ export class AdminSideNavComponent implements OnInit {
|
||||
label: _('admin-side-nav.rule-editor'),
|
||||
show: (this.isIqserDevMode || this.canAccessRulesInDocumine) && this._permissionsService.has(Roles.rules.read),
|
||||
},
|
||||
{
|
||||
screen: 'component-rules',
|
||||
label: _('admin-side-nav.component-rule-editor'),
|
||||
show:
|
||||
this.isDocumine &&
|
||||
(this.isIqserDevMode || this.canAccessRulesInDocumine) &&
|
||||
this._permissionsService.has(Roles.rules.read),
|
||||
},
|
||||
{
|
||||
screen: 'default-colors',
|
||||
label: _('admin-side-nav.default-colors'),
|
||||
@ -127,14 +134,14 @@ export class AdminSideNavComponent implements OnInit {
|
||||
{
|
||||
screen: 'dossier-states',
|
||||
label: _('admin-side-nav.dossier-states'),
|
||||
helpModeKey: 'dossier_states',
|
||||
helpModeKey: this.currentUser.isAdmin ? 'dossier_states' : 'user_dossier_template_dossier_states',
|
||||
show: true,
|
||||
},
|
||||
{
|
||||
screen: 'reports',
|
||||
label: _('admin-side-nav.reports'),
|
||||
show: this._permissionsService.has([Roles.reportTemplates.read]),
|
||||
helpModeKey: 'reports',
|
||||
helpModeKey: this.currentUser.isAdmin ? 'admin_reports' : 'user_dossier_template_reports',
|
||||
},
|
||||
{
|
||||
screen: 'justifications',
|
||||
@ -170,7 +177,10 @@ export class AdminSideNavComponent implements OnInit {
|
||||
],
|
||||
};
|
||||
|
||||
constructor(private readonly _permissionsService: IqserPermissionsService, private readonly _route: ActivatedRoute) {}
|
||||
constructor(
|
||||
private readonly _permissionsService: IqserPermissionsService,
|
||||
private readonly _route: ActivatedRoute,
|
||||
) {}
|
||||
|
||||
@HostBinding('class.smaller') get isSmaller(): boolean {
|
||||
return [AdminSideNavTypes.dossierTemplates, AdminSideNavTypes.entities].includes(this.type);
|
||||
|
||||
@ -106,14 +106,14 @@ export class ConfigService {
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.owner'),
|
||||
checker: dossierOwnerQuickChecker(userId),
|
||||
disabled: entities.filter(dossierOwnerQuickChecker(userId)).length === 0,
|
||||
helpModeKey: 'filter_dossier_list',
|
||||
helpModeKey: 'quick_filter_dossiers',
|
||||
},
|
||||
{
|
||||
id: 'member',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.member'),
|
||||
checker: dossierMemberQuickChecker(userId),
|
||||
disabled: entities.filter(dossierMemberQuickChecker(userId)).length === 0,
|
||||
helpModeKey: 'filter_dossier_list',
|
||||
helpModeKey: 'quick_filter_dossiers',
|
||||
},
|
||||
].map(filter => new NestedFilter(filter));
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
*ngIf="stats as dossierTemplate"
|
||||
[class.empty]="dossierTemplate.isEmpty"
|
||||
[routerLink]="dossierTemplate.isEmpty ? null : ['..', dossierTemplate.dossierTemplateId]"
|
||||
[attr.help-mode-key]="!dossierTemplate.isEmpty ? 'dossier_overview' : null"
|
||||
[attr.help-mode-key]="!dossierTemplate.isEmpty ? 'open_dossier_template' : null"
|
||||
class="dialog"
|
||||
>
|
||||
<ng-container *ngIf="!dossierTemplate.isEmpty; else empty">
|
||||
@ -13,7 +13,7 @@
|
||||
<mat-icon svgIcon="red:archive"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="
|
||||
'dossier-template-stats.archived-dossiers' | translate : { count: dossierTemplate.numberOfArchivedDossiers }
|
||||
'dossier-template-stats.archived-dossiers' | translate: { count: dossierTemplate.numberOfArchivedDossiers }
|
||||
"
|
||||
></span>
|
||||
</div>
|
||||
@ -21,20 +21,18 @@
|
||||
<mat-icon svgIcon="iqser:trash"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="
|
||||
'dossier-template-stats.deleted-dossiers' | translate : { count: dossierTemplate.numberOfDeletedDossiers }
|
||||
'dossier-template-stats.deleted-dossiers' | translate: { count: dossierTemplate.numberOfDeletedDossiers }
|
||||
"
|
||||
></span>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:user"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="'dossier-template-stats.total-people' | translate : { count: dossierTemplate.numberOfPeople }"
|
||||
></span>
|
||||
<span [innerHTML]="'dossier-template-stats.total-people' | translate: { count: dossierTemplate.numberOfPeople }"></span>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="iqser:pages"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="'dossier-template-stats.analyzed-pages' | translate : { count: dossierTemplate.numberOfPages }"
|
||||
[innerHTML]="'dossier-template-stats.analyzed-pages' | translate: { count: dossierTemplate.numberOfPages }"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
@ -44,7 +42,7 @@
|
||||
[config]="translateChartService.translateDossierStates(dossierTemplate.dossiersChartConfig, dossierTemplate.id)"
|
||||
[radius]="63"
|
||||
[strokeWidth]="15"
|
||||
[subtitles]="['dossier-template-stats.active-dossiers' | translate : { count: dossierTemplate.numberOfActiveDossiers }]"
|
||||
[subtitles]="['dossier-template-stats.active-dossiers' | translate: { count: dossierTemplate.numberOfActiveDossiers }]"
|
||||
direction="row"
|
||||
totalType="sum"
|
||||
></redaction-donut-chart>
|
||||
@ -76,7 +74,7 @@
|
||||
*ngIf="permissionsService.canCreateDossier(dossierTemplate)"
|
||||
[label]="'dashboard.empty-template.new-dossier' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
[attr.help-mode-key]="'new_dossier_button'"
|
||||
[attr.help-mode-key]="'new_dossier'"
|
||||
icon="iqser:plus"
|
||||
></iqser-icon-button>
|
||||
</ng-template>
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
>
|
||||
<ng-container slot="right">
|
||||
<redaction-file-download-btn
|
||||
[attr.help-mode-key]="'edit_dossier_in_dossier'"
|
||||
[attr.help-mode-key]="'download_dossier_in_dossier'"
|
||||
[buttonId]="'download-files-btn'"
|
||||
[disabled]="downloadBtnDisabled$ | async"
|
||||
[dossier]="dossier"
|
||||
@ -17,7 +17,7 @@
|
||||
<iqser-circle-button
|
||||
(action)="downloadDossierAsCSV()"
|
||||
*ngIf="permissionsService.canDownloadCsvReport(dossier)"
|
||||
[attr.help-mode-key]="'edit_dossier_in_dossier'"
|
||||
[attr.help-mode-key]="'download_csv'"
|
||||
[disabled]="listingService.areSomeSelected$ | async"
|
||||
[icon]="'iqser:csv'"
|
||||
[tooltip]="'dossier-overview.header-actions.download-csv' | translate"
|
||||
@ -36,7 +36,7 @@
|
||||
<iqser-circle-button
|
||||
(action)="upload.emit()"
|
||||
*ngIf="permissionsService.canUploadFiles(dossier)"
|
||||
[attr.help-mode-key]="'edit_dossier_in_dossier'"
|
||||
[attr.help-mode-key]="'upload_document'"
|
||||
[buttonId]="'upload-document-btn'"
|
||||
[icon]="'iqser:upload'"
|
||||
[tooltip]="'dossier-overview.header-actions.upload-document' | translate"
|
||||
|
||||
@ -35,7 +35,6 @@
|
||||
<div #actionsWrapper class="actions-wrapper">
|
||||
<redaction-file-actions
|
||||
*ngIf="!file.isProcessing"
|
||||
[attr.help-mode-key]="'workflow_view'"
|
||||
[dossier]="dossier"
|
||||
[file]="file"
|
||||
[maxWidth]="width"
|
||||
|
||||
@ -116,7 +116,7 @@ export class ConfigService {
|
||||
action: () => this.#openEditDossierDialog(dossierId),
|
||||
icon: 'iqser:edit',
|
||||
hide: !this.#currentUser.isManager && !this._iqserPermissionsService.has(Roles.dossiers.edit),
|
||||
helpModeKey: 'edit_dossier_in_dossier',
|
||||
helpModeKey: 'edit_dossier',
|
||||
disabled$,
|
||||
},
|
||||
];
|
||||
@ -446,28 +446,28 @@ export class ConfigService {
|
||||
required: true,
|
||||
checker: this._recentlyModifiedChecker,
|
||||
disabled: entities.filter(this._recentlyModifiedChecker).length === 0,
|
||||
helpModeKey: 'filter_document_list',
|
||||
helpModeKey: 'quick_filters_documents',
|
||||
},
|
||||
{
|
||||
id: 'assigned-to-me',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-me'),
|
||||
checker: this._assignedToMeChecker,
|
||||
disabled: entities.filter(this._assignedToMeChecker).length === 0,
|
||||
helpModeKey: 'filter_document_list',
|
||||
helpModeKey: 'quick_filters_documents',
|
||||
},
|
||||
{
|
||||
id: 'unassigned',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.unassigned'),
|
||||
checker: this._unassignedChecker,
|
||||
disabled: entities.filter(this._unassignedChecker).length === 0,
|
||||
helpModeKey: 'filter_document_list',
|
||||
helpModeKey: 'quick_filters_documents',
|
||||
},
|
||||
{
|
||||
id: 'assigned-to-others',
|
||||
label: this._translateService.instant('dossier-overview.quick-filters.assigned-to-others'),
|
||||
checker: this._assignedToOthersChecker,
|
||||
disabled: entities.filter(this._assignedToOthersChecker).length === 0,
|
||||
helpModeKey: 'filter_document_list',
|
||||
helpModeKey: 'quick_filters_documents',
|
||||
},
|
||||
].map(filter => new NestedFilter(filter));
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
[noDataIcon]="'iqser:document'"
|
||||
[noDataText]="'dossier-overview.no-data.title' | translate"
|
||||
[showNoDataButton]="true"
|
||||
[id]="'workflow-view'"
|
||||
addElementColumn="NEW"
|
||||
>
|
||||
<ng-template #workflowItemTemplate let-entity="entity">
|
||||
|
||||
@ -69,7 +69,7 @@ export class ConfigService {
|
||||
hide: !this._permissionsService.canCreateDossier(dossierTemplate),
|
||||
icon: 'iqser:plus',
|
||||
type: 'primary',
|
||||
helpModeKey: 'new_dossier_button',
|
||||
helpModeKey: 'new_dossier',
|
||||
},
|
||||
];
|
||||
}
|
||||
@ -226,14 +226,14 @@ export class ConfigService {
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.owner'),
|
||||
checker: dossierOwnerQuickChecker(userId),
|
||||
disabled: entities.filter(dossierOwnerQuickChecker(userId)).length === 0,
|
||||
helpModeKey: 'filter_dossier_list',
|
||||
helpModeKey: 'quick_filter_dossiers',
|
||||
},
|
||||
{
|
||||
id: 'member',
|
||||
label: this._translateService.instant('dossier-listing.quick-filters.member'),
|
||||
checker: dossierMemberQuickChecker(userId),
|
||||
disabled: entities.filter(dossierMemberQuickChecker(userId)).length === 0,
|
||||
helpModeKey: 'filter_dossier_list',
|
||||
helpModeKey: 'quick_filter_dossiers',
|
||||
},
|
||||
].map(filter => new NestedFilter(filter));
|
||||
}
|
||||
|
||||
@ -30,20 +30,17 @@
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.resize.label' | translate"
|
||||
[type]="buttonType"
|
||||
[attr.help-mode-key]="helpModeKey('resize')"
|
||||
icon="red:resize"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.editRedaction(annotations)"
|
||||
*ngIf="
|
||||
annotationPermissions.canChangeLegalBasis ||
|
||||
annotationPermissions.canRecategorizeAnnotation ||
|
||||
annotationPermissions.canForceRedaction ||
|
||||
annotationPermissions.canForceHint
|
||||
"
|
||||
*ngIf="canEdit"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
|
||||
[type]="buttonType"
|
||||
[attr.help-mode-key]="helpModeKey('edit')"
|
||||
icon="iqser:edit"
|
||||
></iqser-circle-button>
|
||||
|
||||
@ -139,10 +136,11 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="removeOrSuggestRemoveRedaction()"
|
||||
*ngIf="annotationPermissions.canRemoveRedaction"
|
||||
*ngIf="canRemoveRedaction"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
|
||||
[type]="buttonType"
|
||||
[attr.help-mode-key]="helpModeKey('remove')"
|
||||
icon="iqser:trash"
|
||||
></iqser-circle-button>
|
||||
</ng-container>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Component, computed, Input, OnChanges } from '@angular/core';
|
||||
import { HelpModeService, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { getConfig, HelpModeService, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -24,8 +24,6 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
|
||||
styleUrls: ['./annotation-actions.component.scss'],
|
||||
})
|
||||
export class AnnotationActionsComponent implements OnChanges {
|
||||
#annotations: AnnotationWrapper[] = [];
|
||||
protected _annotationId = '';
|
||||
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
|
||||
@Input() tooltipPosition: 'before' | 'above' = 'before';
|
||||
@Input() canPerformAnnotationActions: boolean;
|
||||
@ -33,10 +31,45 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
readonly roles = Roles;
|
||||
annotationPermissions: AnnotationPermissions;
|
||||
isImage = true;
|
||||
protected _annotationId = '';
|
||||
#annotations: AnnotationWrapper[] = [];
|
||||
readonly isVisible = computed(() => {
|
||||
const hidden = this._annotationManager.hidden();
|
||||
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
|
||||
});
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
get annotations(): AnnotationWrapper[] {
|
||||
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 {
|
||||
const documineCanEditRedactions =
|
||||
this.annotationPermissions.canChangeLegalBasis ||
|
||||
this.annotationPermissions.canRecategorizeAnnotation ||
|
||||
this.annotationPermissions.canForceHint ||
|
||||
this.annotationPermissions.canForceRedaction;
|
||||
return this.#isDocumine && this.annotations.length > 1 ? this.annotationPermissions.canEditAnnotations : documineCanEditRedactions;
|
||||
}
|
||||
|
||||
get canRemoveRedaction(): boolean {
|
||||
return (this.#isDocumine || this.annotations.length === 1) && this.annotationPermissions.canRemoveRedaction;
|
||||
}
|
||||
|
||||
get viewerAnnotations() {
|
||||
return this._annotationManager.get(this.#annotations);
|
||||
}
|
||||
|
||||
get resizing() {
|
||||
return this.#annotations?.length === 1 && this.#annotations?.[0].id === this._annotationManager.resizingAnnotationId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly viewModeService: ViewModeService,
|
||||
@ -50,31 +83,12 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
readonly annotationReferencesService: AnnotationReferencesService,
|
||||
) {}
|
||||
|
||||
get annotations(): AnnotationWrapper[] {
|
||||
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 viewerAnnotations() {
|
||||
return this._annotationManager.get(this.#annotations);
|
||||
}
|
||||
|
||||
get resizing() {
|
||||
return this.#annotations?.length === 1 && this.#annotations?.[0].id === this._annotationManager.resizingAnnotationId;
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.#setPermissions();
|
||||
}
|
||||
|
||||
removeOrSuggestRemoveRedaction() {
|
||||
this.annotationActionsService.removeRedaction(this.annotations[0], this.annotationPermissions);
|
||||
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
|
||||
}
|
||||
|
||||
acceptRecommendation() {
|
||||
@ -113,4 +127,8 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
this._iqserPermissionsService,
|
||||
);
|
||||
}
|
||||
|
||||
helpModeKey(action: string) {
|
||||
return this.#isDocumine ? `${action}_annotation` : '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { PdfProxyService } from '../../services/pdf-proxy.service';
|
||||
import { ActionsHelpModeKeys } from '../../utils/constants';
|
||||
import { ListItem } from '@models/file/list-item';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { getConfig } from '@iqser/common-ui';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-wrapper',
|
||||
@ -16,6 +17,7 @@ export class AnnotationWrapperComponent implements OnChanges {
|
||||
@HostBinding('attr.annotation-id') annotationId: string;
|
||||
@HostBinding('class.active') active = false;
|
||||
actionsHelpModeKey?: string;
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
protected readonly _pdfProxyService = inject(PdfProxyService);
|
||||
protected readonly _multiSelectService = inject(MultiSelectService);
|
||||
|
||||
@ -26,11 +28,14 @@ export class AnnotationWrapperComponent implements OnChanges {
|
||||
}
|
||||
|
||||
#getActionsHelpModeKey(): string {
|
||||
const type = this.annotation.item.typeLabel?.split('.')[1];
|
||||
const typeValue = this.annotation.item.typeValue;
|
||||
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
|
||||
return ActionsHelpModeKeys[`${type}-${typeValue}`];
|
||||
if (!this.#isDocumine) {
|
||||
const type = this.annotation.item.typeLabel?.split('.')[1];
|
||||
const typeValue = this.annotation.item.typeValue;
|
||||
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
|
||||
return ActionsHelpModeKeys[`${type}-${typeValue}`];
|
||||
}
|
||||
return ActionsHelpModeKeys[type];
|
||||
}
|
||||
return ActionsHelpModeKeys[type];
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<div
|
||||
(click)="multiSelectService.activate()"
|
||||
*ngIf="multiSelectService.enabled() && multiSelectService.inactive()"
|
||||
[attr.help-mode-key]="'workload_in_editor'"
|
||||
[attr.help-mode-key]="'workload_bulk_selection'"
|
||||
class="all-caps-label primary pointer"
|
||||
translate="file-preview.tabs.annotations.select"
|
||||
></div>
|
||||
@ -28,7 +28,7 @@
|
||||
<iqser-popup-filter
|
||||
*ngIf="documentInfoService.hidden()"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[attr.help-mode-key]="'workload_in_editor'"
|
||||
[attr.help-mode-key]="'workload_filter'"
|
||||
[fileId]="state.file()?.id"
|
||||
[primaryFiltersSlug]="'primaryFilters'"
|
||||
[secondaryFiltersSlug]="'secondaryFilters'"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div *ngIf="viewedPages$ | async as viewedPages" class="pages" id="pages">
|
||||
<div *ngIf="viewedPages$ | async as viewedPages" class="pages" id="pages" [attr.help-mode-key]="'workload_page_list'">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of pages; trackBy: trackBy"
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
<iqser-circle-button
|
||||
(action)="editingReviewer = true"
|
||||
*ngIf="_canAssignOrUnassign() && !!file.assignee"
|
||||
[attr.help-mode-key]="'document_features_in_editor'"
|
||||
[attr.help-mode-key]="'editor_assign_user'"
|
||||
[buttonId]="'change-assignee'"
|
||||
[icon]="'iqser:edit'"
|
||||
[tooltip]="assignTooltip() | translate"
|
||||
|
||||
@ -3,16 +3,26 @@
|
||||
<div [translate]="'edit-redaction.dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<div *ngIf="redactedText" class="iqser-input-group w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.redacted-text'" class="selected-text"></label>
|
||||
{{ redactedText }}
|
||||
<div *ngIf="showList">
|
||||
<label
|
||||
[translate]="'edit-redaction.dialog.content.redacted-text'"
|
||||
[translateParams]="{ length: redactedTexts.length }"
|
||||
class="selected-text"
|
||||
></label>
|
||||
<cdk-virtual-scroll-viewport
|
||||
[itemSize]="16"
|
||||
[ngStyle]="{ height: redactedTexts.length <= 5 ? 16 * redactedTexts.length + 'px' : 80 + 'px' }"
|
||||
>
|
||||
<ul *cdkVirtualFor="let text of redactedTexts">
|
||||
<li>{{ text }}</li>
|
||||
</ul>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group required w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.type'"></label>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-select formControlName="type">
|
||||
<mat-select formControlName="type" [placeholder]="'edit-redaction.dialog.content.unchanged' | translate">
|
||||
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
|
||||
<mat-option
|
||||
*ngFor="let dictionary of dictionaries"
|
||||
@ -29,7 +39,11 @@
|
||||
<div class="iqser-input-group w-450">
|
||||
<label [translate]="'edit-redaction.dialog.content.comment'"></label>
|
||||
<textarea
|
||||
[placeholder]="'edit-redaction.dialog.content.comment-placeholder' | translate"
|
||||
[placeholder]="
|
||||
redactedTexts.length === 1
|
||||
? ('edit-redaction.dialog.content.comment-placeholder' | translate)
|
||||
: ('edit-redaction.dialog.content.unchanged' | translate)
|
||||
"
|
||||
formControlName="comment"
|
||||
iqserHasScrollbar
|
||||
name="comment"
|
||||
@ -40,7 +54,12 @@
|
||||
</div>
|
||||
|
||||
<div class="dialog-actions">
|
||||
<iqser-icon-button [label]="'edit-redaction.dialog.actions.save' | translate" [submit]="true" [type]="iconButtonTypes.primary">
|
||||
<iqser-icon-button
|
||||
[label]="'edit-redaction.dialog.actions.save' | translate"
|
||||
[submit]="true"
|
||||
[type]="iconButtonTypes.primary"
|
||||
[disabled]="!changed"
|
||||
>
|
||||
</iqser-icon-button>
|
||||
|
||||
<div [translate]="'edit-redaction.dialog.actions.cancel'" class="all-caps-label cancel" mat-dialog-close></div>
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
@use 'common-mixins';
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
margin-top: 8px;
|
||||
@include common-mixins.scroll-bar;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
padding-right: 4px;
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { EditRedactionData, EditRedactResult } from '../../../utils/dialog-types
|
||||
|
||||
@Component({
|
||||
templateUrl: 'edit-annotation-dialog.component.html',
|
||||
styleUrls: ['edit-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class EditAnnotationDialogComponent
|
||||
extends IqserDialogComponent<EditAnnotationDialogComponent, EditRedactionData, EditRedactResult>
|
||||
@ -17,7 +18,7 @@ export class EditAnnotationDialogComponent
|
||||
{
|
||||
readonly roles = Roles;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly redactedText: string;
|
||||
readonly redactedTexts: string[];
|
||||
dictionaries: Dictionary[] = [];
|
||||
form: UntypedFormGroup;
|
||||
readonly #dossier: Dossier;
|
||||
@ -32,9 +33,9 @@ export class EditAnnotationDialogComponent
|
||||
super();
|
||||
this.#dossier = _activeDossiersService.find(this.data.dossierId);
|
||||
const annotations = this.data.annotations;
|
||||
const firstEntry = annotations[0];
|
||||
this.redactedText = annotations.length === 1 ? firstEntry.value : null;
|
||||
this.redactedTexts = annotations.map(annotation => annotation.value);
|
||||
this.form = this.#getForm();
|
||||
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||
}
|
||||
|
||||
get displayedDictionaryLabel() {
|
||||
@ -45,6 +46,10 @@ export class EditAnnotationDialogComponent
|
||||
return null;
|
||||
}
|
||||
|
||||
get showList() {
|
||||
return this.data.annotations.every(annotation => annotation.isSkipped || annotation.isRedacted);
|
||||
}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.#setTypes();
|
||||
}
|
||||
@ -66,9 +71,10 @@ export class EditAnnotationDialogComponent
|
||||
}
|
||||
|
||||
#getForm(): UntypedFormGroup {
|
||||
const sameType = new Set(this.data.annotations.map(annotation => annotation.type)).size === 1;
|
||||
return this._formBuilder.group({
|
||||
comment: [null],
|
||||
type: [this.data.annotations[0].type],
|
||||
type: [sameType ? this.data.annotations[0].type : null],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,41 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="save()" [formGroup]="form">
|
||||
<div [innerHTML]="'remove-annotation.dialog.title' | translate" class="dialog-header heading-l"></div>
|
||||
<div
|
||||
[innerHTML]="'remove-annotation.dialog.title' | translate: { count: data.redactions.length }"
|
||||
class="dialog-header heading-l"
|
||||
></div>
|
||||
|
||||
<div class="dialog-content redaction">
|
||||
<div *ngIf="data.redactions.length > 1">
|
||||
<label
|
||||
[translateParams]="{ length: redactedTexts.length }"
|
||||
[translate]="'remove-annotation.dialog.content.redacted-text'"
|
||||
class="selected-text"
|
||||
></label>
|
||||
|
||||
<cdk-virtual-scroll-viewport
|
||||
[itemSize]="16"
|
||||
[ngStyle]="{ height: redactedTexts.length <= 5 ? 16 * redactedTexts.length + 'px' : 80 + 'px' }"
|
||||
>
|
||||
<ul *cdkVirtualFor="let text of redactedTexts; let idx = index">
|
||||
<li>
|
||||
{{
|
||||
(isFalsePositive
|
||||
? 'remove-annotation.dialog.content.list-item-false-positive'
|
||||
: 'remove-annotation.dialog.content.list-item'
|
||||
)
|
||||
| translate
|
||||
: {
|
||||
text: text,
|
||||
context: data.falsePositiveContext[idx]
|
||||
}
|
||||
| replaceNbsp
|
||||
}}
|
||||
</li>
|
||||
</ul>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
</div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<iqser-details-radio [options]="options" formControlName="option"></iqser-details-radio>
|
||||
|
||||
<div class="iqser-input-group w-450">
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
cdk-virtual-scroll-viewport {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
:host ::ng-deep .cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
li {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
list-style-position: inside;
|
||||
overflow: hidden;
|
||||
}
|
||||
@ -1,13 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DetailsRadioOption, IconButtonTypes } from '@iqser/common-ui';
|
||||
import { DetailsRadioOption, IconButtonTypes, IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { FormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
import { IqserDialogComponent } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { RemoveAnnotationData, RemoveAnnotationResult } from '../../../utils/dialog-types';
|
||||
import { getRemoveRedactionOptions, RemoveAnnotationOption, RemoveAnnotationOptions } from '../../../utils/dialog-options';
|
||||
|
||||
@Component({
|
||||
templateUrl: './remove-annotation-dialog.component.html',
|
||||
templateUrl: 'remove-annotation-dialog.component.html',
|
||||
styleUrls: ['remove-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
|
||||
RemoveAnnotationDialogComponent,
|
||||
@ -16,13 +15,12 @@ export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
|
||||
> {
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly options: DetailsRadioOption<RemoveAnnotationOption>[];
|
||||
readonly redactedTexts: string[];
|
||||
|
||||
form!: UntypedFormGroup;
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder, private readonly _permissionsService: PermissionsService) {
|
||||
super();
|
||||
this.options = getRemoveRedactionOptions(this.data, true);
|
||||
this.form = this.#getForm();
|
||||
get isFalsePositive(): boolean {
|
||||
return this.form.get('option').value.value === RemoveAnnotationOptions.FALSE_POSITIVE;
|
||||
}
|
||||
|
||||
get #applyToAllDossiers(): boolean {
|
||||
@ -30,6 +28,13 @@ export class RemoveAnnotationDialogComponent extends IqserDialogComponent<
|
||||
return selectedOption === RemoveAnnotationOptions.IN_DOSSIER || selectedOption === RemoveAnnotationOptions.FALSE_POSITIVE;
|
||||
}
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder) {
|
||||
super();
|
||||
this.options = getRemoveRedactionOptions(this.data, true);
|
||||
this.redactedTexts = this.data.redactions.map(annotation => annotation.value);
|
||||
this.form = this.#getForm();
|
||||
}
|
||||
|
||||
save(): void {
|
||||
this.dialogRef.close({ ...this.form.getRawValue(), applyToAllDossiers: this.#applyToAllDossiers });
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
<div *ngIf="isHintDialog" class="dialog-header heading-l" [translate]="'manual-annotation.dialog.header.force-hint'"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div *ngIf="!isHintDialog" class="iqser-input-group required w-400">
|
||||
<div *ngIf="!isHintDialog && !isDocumine" class="iqser-input-group required w-400">
|
||||
<label [translate]="'manual-annotation.dialog.content.reason'"></label>
|
||||
<mat-form-field>
|
||||
<mat-select
|
||||
@ -19,7 +19,7 @@
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div *ngIf="!isHintDialog" class="iqser-input-group w-400">
|
||||
<div *ngIf="!isHintDialog && !isDocumine" class="iqser-input-group w-400">
|
||||
<label [translate]="'manual-annotation.dialog.content.legalBasis'"></label>
|
||||
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { UntypedFormGroup, Validators } from '@angular/forms';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
import { BaseDialogComponent, getConfig } from '@iqser/common-ui';
|
||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||
import { Dossier, ILegalBasisChangeRequest } from '@red/domain';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
@ -19,6 +19,7 @@ export interface LegalBasisOption {
|
||||
styleUrls: ['./force-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class ForceAnnotationDialogComponent extends BaseDialogComponent implements OnInit {
|
||||
readonly isDocumine = getConfig().IS_DOCUMINE;
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
|
||||
constructor(
|
||||
|
||||
@ -15,16 +15,17 @@ export class RemoveRedactionDialogComponent extends IqserDialogComponent<
|
||||
RemoveRedactionData,
|
||||
RemoveRedactionResult
|
||||
> {
|
||||
#applyToAllDossiers: boolean;
|
||||
readonly iconButtonTypes = IconButtonTypes;
|
||||
readonly options: DetailsRadioOption<RemoveRedactionOption>[];
|
||||
readonly recommendation = this.data.redaction.isRecommendation;
|
||||
readonly recommendation;
|
||||
readonly hint: boolean;
|
||||
form!: UntypedFormGroup;
|
||||
hint: boolean;
|
||||
#applyToAllDossiers: boolean;
|
||||
|
||||
constructor(private readonly _formBuilder: FormBuilder) {
|
||||
super();
|
||||
this.hint = this.data.redaction.hint;
|
||||
this.recommendation = this.data.redactions.every(r => r.isRecommendation);
|
||||
this.hint = this.data.redactions.every(r => r.hint);
|
||||
this.options = getRemoveRedactionOptions(this.data);
|
||||
this.form = this.#getForm();
|
||||
this.#applyToAllDossiers = this.data.applyToAllDossiers ?? true;
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
<div [translate]="'rss-dialog.title'" class="dialog-header heading-l"></div>
|
||||
|
||||
<hr />
|
||||
<div class="dialog-content">
|
||||
<div class="dialog-content" id="scm-edit">
|
||||
<div *ngIf="scmData() as scmEntry" class="table output-data">
|
||||
<div class="table-header">{{ 'rss-dialog.table-header.component' | translate }}</div>
|
||||
<div class="table-header">{{ 'rss-dialog.table-header.value' | translate }}</div>
|
||||
<div class="table-header">{{ 'rss-dialog.table-header.transformation-rule' | translate }}</div>
|
||||
<div class="table-header">{{ 'rss-dialog.table-header.annotation-references' | translate }}</div>
|
||||
|
||||
<ng-container *ngFor="let entry of scmEntry.result | keyvalue : originalOrder; let index = index">
|
||||
<ng-container *ngFor="let entry of scmEntry.result | keyvalue: originalOrder; let index = index">
|
||||
<div class="bold">{{ entry.key }}</div>
|
||||
<div [id]="getValueCellId(index)">
|
||||
<iqser-editable-input
|
||||
@ -21,13 +21,15 @@
|
||||
[parentId]="getValueCellId(index)"
|
||||
[saveTooltip]="'rss-dialog.actions.save' | translate"
|
||||
[value]="entry.value.value ?? entry.value.originalValue"
|
||||
[helpModeKey]="'scm_edit_DIALOG'"
|
||||
>
|
||||
<ng-container slot="editing">
|
||||
<iqser-circle-button
|
||||
(action)="undo(entry.value.originalKey)"
|
||||
*ngIf="entry.value.value && canEdit"
|
||||
[showDot]="true"
|
||||
[tooltip]="'rss-dialog.actions.undo' | translate : { value: entry.value.originalValue } | replaceNbsp"
|
||||
[tooltip]="'rss-dialog.actions.undo' | translate: { value: entry.value.originalValue } | replaceNbsp"
|
||||
[attr.help-mode-key]="'scm_undo_DIALOG'"
|
||||
class="ml-2"
|
||||
icon="red:undo"
|
||||
></iqser-circle-button>
|
||||
@ -65,12 +67,14 @@
|
||||
[label]="'rss-dialog.actions.export-json' | translate"
|
||||
[submit]="true"
|
||||
[type]="iconButtonTypes.primary"
|
||||
[attr.help-mode-key]="'scm_export_DIALOG'"
|
||||
></iqser-icon-button>
|
||||
|
||||
<iqser-icon-button
|
||||
(action)="exportXML()"
|
||||
[label]="'rss-dialog.actions.export-xml' | translate"
|
||||
[type]="iconButtonTypes.primary"
|
||||
[attr.help-mode-key]="'scm_export_DIALOG'"
|
||||
></iqser-icon-button>
|
||||
|
||||
<iqser-icon-button
|
||||
|
||||
@ -30,11 +30,12 @@
|
||||
class="ml-8"
|
||||
icon="red:extract"
|
||||
tooltipPosition="below"
|
||||
[attr.help-mode-key]="'editor_scm'"
|
||||
></iqser-circle-button>
|
||||
|
||||
<redaction-file-actions
|
||||
[dossier]="state.dossier()"
|
||||
[fileActionsHelpModeKey]="'editor_document_features'"
|
||||
[helpModeKeyPrefix]="'editor'"
|
||||
[file]="file"
|
||||
[minWidth]="width"
|
||||
type="file-preview"
|
||||
@ -53,6 +54,7 @@
|
||||
[icon]="fullScreen ? 'red:exit-fullscreen' : 'red:fullscreen'"
|
||||
[tooltip]="'file-preview.fullscreen' | translate"
|
||||
class="ml-2"
|
||||
[attr.help-mode-key]="'editor_full_screen'"
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- Dev Mode Features-->
|
||||
@ -73,6 +75,7 @@
|
||||
[tooltip]="'common.close' | translate"
|
||||
class="ml-8"
|
||||
icon="iqser:close"
|
||||
[attr.help-mode-key]="'editor_close'"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -338,6 +338,7 @@ export class FilePreviewScreenComponent
|
||||
document.documentElement.addEventListener('fullscreenchange', this.fullscreenListener);
|
||||
|
||||
this.#openRssDialogIfDefault();
|
||||
this._viewerHeaderService.resetLayers();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
||||
@ -71,6 +71,7 @@ import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
|
||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||
import { ManualRedactionService } from './services/manual-redaction.service';
|
||||
import { TablesService } from './services/tables.service';
|
||||
import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
|
||||
|
||||
const routes: IqserRoutes = [
|
||||
{
|
||||
@ -154,6 +155,7 @@ const components = [
|
||||
IqserDenyDirective,
|
||||
TenantPipe,
|
||||
LogPipe,
|
||||
ReplaceNbspPipe,
|
||||
],
|
||||
providers: [FilePreviewDialogService, ManualRedactionService, DocumentUnloadedGuard, TablesService],
|
||||
})
|
||||
|
||||
@ -113,10 +113,10 @@ export class AnnotationActionsService {
|
||||
}));
|
||||
requests.push(this._manualRedactionService.changeLegalBasis(changeLegalBasisBody, dossierId, fileId));
|
||||
}
|
||||
if (result.type !== annotations[0].type || this.#isDocumine) {
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(({ id }) => ({
|
||||
annotationId: id,
|
||||
type: result.type,
|
||||
if (this.#isDocumine) {
|
||||
const recategorizeBody: List<IRecategorizationRequest> = annotations.map(annotation => ({
|
||||
annotationId: annotation.id,
|
||||
type: result.type ?? annotation.type,
|
||||
}));
|
||||
requests.push(this._manualRedactionService.recategorizeRedactions(recategorizeBody, dossierId, fileId));
|
||||
}
|
||||
@ -134,7 +134,7 @@ export class AnnotationActionsService {
|
||||
await this.#processObsAndEmit(zip(requests).pipe(log()));
|
||||
}
|
||||
|
||||
async removeRedaction(redaction: AnnotationWrapper, permissions: AnnotationPermissions) {
|
||||
async removeRedaction(redactions: AnnotationWrapper[], permissions: AnnotationPermissions) {
|
||||
const removePermissions: RemoveRedactionPermissions = {
|
||||
canRemoveOnlyHere: permissions.canRemoveOnlyHere,
|
||||
canRemoveFromDictionary: permissions.canRemoveFromDictionary,
|
||||
@ -144,9 +144,9 @@ export class AnnotationActionsService {
|
||||
const isApprover = this._permissionsService.isApprover(this._state.dossier());
|
||||
|
||||
const data = {
|
||||
redaction,
|
||||
redactions,
|
||||
dossier: this._state.dossier(),
|
||||
falsePositiveContext: this.#getFalsePositiveText(redaction),
|
||||
falsePositiveContext: redactions.map(r => this.#getFalsePositiveText(r)),
|
||||
permissions: removePermissions,
|
||||
applyToAllDossiers: isApprover ? dossierTemplate.applyDictionaryUpdatesToAllDossiersByDefault : false,
|
||||
isApprover,
|
||||
@ -162,9 +162,9 @@ export class AnnotationActionsService {
|
||||
result.option.value === RemoveRedactionOptions.FALSE_POSITIVE ||
|
||||
result.option.value === RemoveRedactionOptions.DO_NOT_RECOMMEND
|
||||
) {
|
||||
this.#setAsFalsePositive(redaction, result);
|
||||
this.#setAsFalsePositive(redactions, result);
|
||||
} else {
|
||||
this.#removeRedaction(redaction, result);
|
||||
this.#removeRedaction(redactions, result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,8 +393,8 @@ export class AnnotationActionsService {
|
||||
return words;
|
||||
}
|
||||
|
||||
#setAsFalsePositive(redaction: AnnotationWrapper, dialogResult: RemoveRedactionResult) {
|
||||
const request = {
|
||||
#setAsFalsePositive(redactions: AnnotationWrapper[], dialogResult: RemoveRedactionResult) {
|
||||
const requests = redactions.map(redaction => ({
|
||||
sourceId: redaction.id,
|
||||
value: this.#getFalsePositiveText(redaction),
|
||||
type: redaction.type,
|
||||
@ -406,24 +406,24 @@ export class AnnotationActionsService {
|
||||
? DictionaryEntryTypes.FALSE_RECOMMENDATION
|
||||
: DictionaryEntryTypes.FALSE_POSITIVE,
|
||||
comment: dialogResult.comment ? { text: dialogResult.comment } : null,
|
||||
};
|
||||
}));
|
||||
const { dossierId, fileId } = this._state;
|
||||
|
||||
this.#processObsAndEmit(this._manualRedactionService.addAnnotation([request], dossierId, fileId)).then();
|
||||
this.#processObsAndEmit(this._manualRedactionService.addAnnotation(requests, dossierId, fileId)).then();
|
||||
}
|
||||
|
||||
#removeRedaction(redaction: AnnotationWrapper, dialogResult: RemoveRedactionResult) {
|
||||
#removeRedaction(redactions: AnnotationWrapper[], dialogResult: RemoveRedactionResult) {
|
||||
const removeFromDictionary = dialogResult.option.value === RemoveRedactionOptions.IN_DOSSIER;
|
||||
const body = {
|
||||
const body = redactions.map(redaction => ({
|
||||
annotationId: redaction.id,
|
||||
comment: dialogResult.comment,
|
||||
removeFromDictionary,
|
||||
removeFromAllDossiers: !!dialogResult.option.extraOption?.checked || !!dialogResult.applyToAllDossiers,
|
||||
};
|
||||
}));
|
||||
// todo: might not be correct, probably shouldn't get to this point if they are not all the same
|
||||
const isHint = redactions.every(r => r.isHint);
|
||||
const { dossierId, fileId } = this._state;
|
||||
this.#processObsAndEmit(
|
||||
this._manualRedactionService.removeRedaction([body], dossierId, fileId, removeFromDictionary, redaction.isHint),
|
||||
).then();
|
||||
this.#processObsAndEmit(this._manualRedactionService.removeRedaction(body, dossierId, fileId, removeFromDictionary, isHint)).then();
|
||||
}
|
||||
|
||||
#getRemoveRedactionDialog(data: RemoveRedactionData) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { effect, Injectable, Signal, signal } from '@angular/core';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { EntitiesService, Toaster } from '@iqser/common-ui';
|
||||
import { EntitiesService, getConfig, Toaster } from '@iqser/common-ui';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import {
|
||||
ChangeType,
|
||||
@ -50,6 +50,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
||||
readonly earmarks: Signal<Map<number, AnnotationWrapper[]>>;
|
||||
readonly annotations: Signal<AnnotationWrapper[]>;
|
||||
readonly annotations$: Observable<AnnotationWrapper[]>;
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
constructor(
|
||||
private readonly _state: FilePreviewStateService,
|
||||
@ -220,6 +221,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
||||
changeLogValues.changeLogType,
|
||||
redactionLog.legalBasis ?? [],
|
||||
!!dictionary?.hint,
|
||||
this.#isDocumine,
|
||||
);
|
||||
|
||||
if (entry.sourceId) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { inject, Injectable, NgZone } from '@angular/core';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
|
||||
import { BASE_HREF_FN } from '@iqser/common-ui/lib/utils';
|
||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
@ -21,6 +21,7 @@ export class PdfAnnotationActionsService {
|
||||
readonly #annotationActionsService = inject(AnnotationActionsService);
|
||||
readonly #iqserPermissionsService = inject(IqserPermissionsService);
|
||||
readonly #annotationManager = inject(REDAnnotationManager);
|
||||
readonly #isDocumine = getConfig().IS_DOCUMINE;
|
||||
|
||||
get(annotations: AnnotationWrapper[]): IHeaderElement[] {
|
||||
const availableActions: IHeaderElement[] = [];
|
||||
@ -47,13 +48,13 @@ export class PdfAnnotationActionsService {
|
||||
|
||||
availableActions.push(resizeButton);
|
||||
}
|
||||
|
||||
if (
|
||||
const canEditRedactions =
|
||||
permissions.canChangeLegalBasis ||
|
||||
permissions.canRecategorizeAnnotation ||
|
||||
permissions.canForceHint ||
|
||||
permissions.canForceRedaction
|
||||
) {
|
||||
permissions.canForceRedaction;
|
||||
const canEdit = this.#isDocumine && annotations.length > 1 ? permissions.canEditAnnotations : canEditRedactions;
|
||||
if (canEdit) {
|
||||
const editButton = this.#getButton('edit', _('annotation-actions.edit-redaction.label'), () =>
|
||||
this.#annotationActionsService.editRedaction(annotations),
|
||||
);
|
||||
@ -83,7 +84,7 @@ export class PdfAnnotationActionsService {
|
||||
|
||||
if (permissions.canRemoveRedaction) {
|
||||
const removeRedactionButton = this.#getButton('trash', _('annotation-actions.remove-annotation.remove-redaction'), () =>
|
||||
this.#annotationActionsService.removeRedaction(annotations[0], permissions),
|
||||
this.#annotationActionsService.removeRedaction(annotations, permissions),
|
||||
);
|
||||
availableActions.push(removeRedactionButton);
|
||||
}
|
||||
|
||||
@ -149,15 +149,16 @@ export const getRemoveRedactionOptions = (
|
||||
isDocumine: boolean = false,
|
||||
): DetailsRadioOption<RemoveRedactionOption>[] => {
|
||||
const translations = isDocumine ? removeAnnotationTranslations : removeRedactionTranslations;
|
||||
const { permissions, redaction, applyToAllDossiers, isApprover, falsePositiveContext } = data;
|
||||
const { permissions, redactions, applyToAllDossiers, isApprover, falsePositiveContext } = data;
|
||||
const isBulk = redactions.length > 1;
|
||||
|
||||
const options: DetailsRadioOption<RemoveRedactionOption>[] = [];
|
||||
if (permissions.canRemoveOnlyHere) {
|
||||
options.push({
|
||||
label: translations.ONLY_HERE.label,
|
||||
description: translations.ONLY_HERE.description,
|
||||
description: isBulk ? translations.ONLY_HERE.descriptionBulk : translations.ONLY_HERE.description,
|
||||
descriptionParams: {
|
||||
value: redaction.value,
|
||||
value: redactions[0].value,
|
||||
},
|
||||
icon: PIN_ICON,
|
||||
value: RemoveRedactionOptions.ONLY_HERE,
|
||||
@ -165,9 +166,9 @@ export const getRemoveRedactionOptions = (
|
||||
}
|
||||
if (permissions.canRemoveFromDictionary) {
|
||||
options.push({
|
||||
label: translations.IN_DOSSIER.label,
|
||||
description: translations.IN_DOSSIER.description,
|
||||
descriptionParams: { value: redaction.value, type: redaction.type },
|
||||
label: isBulk ? translations.IN_DOSSIER.labelBulk : translations.IN_DOSSIER.label,
|
||||
description: isBulk ? translations.IN_DOSSIER.descriptionBulk : translations.IN_DOSSIER.description,
|
||||
descriptionParams: { value: redactions[0].value, type: redactions[0].type },
|
||||
icon: FOLDER_ICON,
|
||||
value: RemoveRedactionOptions.IN_DOSSIER,
|
||||
extraOption: !isDocumine
|
||||
@ -180,11 +181,15 @@ export const getRemoveRedactionOptions = (
|
||||
});
|
||||
}
|
||||
if (permissions.canMarkAsFalsePositive) {
|
||||
if (data.redaction.isRecommendation) {
|
||||
if (data.redactions[0].isRecommendation) {
|
||||
options.push({
|
||||
label: translations.DO_NOT_RECOMMEND.label,
|
||||
description: translations.DO_NOT_RECOMMEND.description,
|
||||
descriptionParams: { value: redaction.value, type: redaction.type, context: falsePositiveContext },
|
||||
description: isBulk ? translations.DO_NOT_RECOMMEND.descriptionBulk : translations.DO_NOT_RECOMMEND.description,
|
||||
descriptionParams: {
|
||||
value: redactions[0].value,
|
||||
type: redactions[0].type,
|
||||
context: falsePositiveContext[0],
|
||||
},
|
||||
icon: FOLDER_ICON,
|
||||
value: RemoveRedactionOptions.DO_NOT_RECOMMEND,
|
||||
extraOption: !isDocumine
|
||||
@ -198,8 +203,12 @@ export const getRemoveRedactionOptions = (
|
||||
} else {
|
||||
options.push({
|
||||
label: translations.FALSE_POSITIVE.label,
|
||||
description: translations.FALSE_POSITIVE.description,
|
||||
descriptionParams: { value: redaction.value, type: redaction.type, context: falsePositiveContext },
|
||||
description: isBulk ? translations.FALSE_POSITIVE.descriptionBulk : translations.FALSE_POSITIVE.description,
|
||||
descriptionParams: {
|
||||
value: redactions[0].value,
|
||||
type: redactions[0].type,
|
||||
context: falsePositiveContext[0],
|
||||
},
|
||||
icon: REMOVE_FROM_DICT_ICON,
|
||||
value: RemoveRedactionOptions.FALSE_POSITIVE,
|
||||
extraOption: !isDocumine
|
||||
|
||||
@ -71,9 +71,9 @@ export interface RemoveRedactionPermissions {
|
||||
}
|
||||
|
||||
export interface RemoveRedactionData {
|
||||
redaction: AnnotationWrapper;
|
||||
redactions: AnnotationWrapper[];
|
||||
dossier: Dossier;
|
||||
falsePositiveContext: string;
|
||||
falsePositiveContext: string[];
|
||||
permissions: RemoveRedactionPermissions;
|
||||
applyToAllDossiers: boolean;
|
||||
isApprover: boolean;
|
||||
|
||||
@ -33,6 +33,11 @@ export class LayersService {
|
||||
return tooltipsDisabled ? this.#enableIcon : this.#disableIcon;
|
||||
}
|
||||
|
||||
resetLayers() {
|
||||
this._active = false;
|
||||
this.#updateButton();
|
||||
}
|
||||
|
||||
async toggleLayers(): Promise<void> {
|
||||
const layers = await this._documentViewer.document.getLayersArray();
|
||||
layers.forEach(layer => {
|
||||
@ -45,6 +50,10 @@ export class LayersService {
|
||||
this._documentViewer.refreshAndUpdateView();
|
||||
|
||||
this._active = !this._active;
|
||||
this.#updateButton();
|
||||
}
|
||||
|
||||
#updateButton() {
|
||||
this._pdf.instance.UI.updateElement(HeaderElements.TOGGLE_LAYERS, {
|
||||
title: this.toggleLayersBtnTitle,
|
||||
img: this.toggleLayersBtnIcon,
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { inject, Injectable, NgZone } from '@angular/core';
|
||||
import { getConfig, IqserPermissionsService, isIqserDevMode } from '@iqser/common-ui';
|
||||
import { getConfig, HelpModeService, IqserPermissionsService, isIqserDevMode } from '@iqser/common-ui';
|
||||
import { BASE_HREF_FN } from '@iqser/common-ui/lib/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { IHeaderElement, RotationTypes } from '@red/domain';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { fromEvent, Observable, Subject } from 'rxjs';
|
||||
import { fromEvent, merge, Observable, Subject } from 'rxjs';
|
||||
import { filter, map, tap } from 'rxjs/operators';
|
||||
import { HeaderElements, HeaderElementType } from '../../file-preview/utils/constants';
|
||||
import { ROTATION_ACTION_BUTTONS, ROTATION_BUTTONS, ViewerEvents } from '../utils/constants';
|
||||
@ -56,6 +56,7 @@ export class ViewerHeaderService {
|
||||
private readonly _layersService: LayersService,
|
||||
private readonly _readableRedactionsService: ReadableRedactionsService,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _helpModeService: HelpModeService,
|
||||
) {
|
||||
this.events$ = this.#events$.asObservable();
|
||||
}
|
||||
@ -224,9 +225,9 @@ export class ViewerHeaderService {
|
||||
}
|
||||
|
||||
get #toggleLoadAnnotations$() {
|
||||
return this.expandedPanelEvent$.pipe(
|
||||
tap(isVisible =>
|
||||
isVisible ? this.enable([HeaderElements.LOAD_ALL_ANNOTATIONS]) : this.disable([HeaderElements.LOAD_ALL_ANNOTATIONS]),
|
||||
return merge(this.expandedPanelEvent$, this._helpModeService.isHelpModeActive$).pipe(
|
||||
tap(enable =>
|
||||
enable ? this.enable([HeaderElements.LOAD_ALL_ANNOTATIONS]) : this.disable([HeaderElements.LOAD_ALL_ANNOTATIONS]),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -376,4 +377,8 @@ export class ViewerHeaderService {
|
||||
this.disable(ROTATION_ACTION_BUTTONS);
|
||||
}
|
||||
}
|
||||
|
||||
resetLayers() {
|
||||
this._layersService.resetLayers();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<iqser-circle-button
|
||||
(action)="openEditDossierDialog(dossier.id)"
|
||||
*allow="roles.dossiers.read; if: currentUser.isUser"
|
||||
[attr.help-mode-key]="'edit_dossier_dossier_info'"
|
||||
[attr.help-mode-key]="'edit_dossier'"
|
||||
[icon]="
|
||||
((iqserPermissionsService.has$(roles.dossiers.edit) | async) && currentUser.isManager) || canEditDossierDictionary
|
||||
? 'iqser:edit'
|
||||
|
||||
@ -11,13 +11,15 @@
|
||||
</ng-container>
|
||||
|
||||
<ng-template #actions (longPress)="forceReanalysisAction($event)" redactionLongPress>
|
||||
<div [attr.help-mode-key]="fileActionsHelpModeKey" class="file-actions">
|
||||
<div class="file-actions">
|
||||
<redaction-expandable-file-actions
|
||||
[actions]="buttons"
|
||||
[id]="'actions-for-' + file.fileId"
|
||||
[maxWidth]="maxWidth"
|
||||
[minWidth]="minWidth"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[helpModeKeyPrefix]="helpModeKeyPrefix"
|
||||
[isDossierOverviewWorkflow]="isDossierOverviewWorkflow"
|
||||
></redaction-expandable-file-actions>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@ -41,7 +41,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
@Input({ required: true }) type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||
@Input() maxWidth: number;
|
||||
@Input() minWidth: number;
|
||||
@Input() fileActionsHelpModeKey: 'document_features_in_dossier' | 'editor_document_features' = 'document_features_in_dossier';
|
||||
@Input() helpModeKeyPrefix: 'dossier' | 'editor' = 'dossier';
|
||||
readonly currentUser = getCurrentUser<User>();
|
||||
toggleTooltip?: string;
|
||||
assignTooltip?: string;
|
||||
@ -112,6 +112,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.delete.action'),
|
||||
icon: 'iqser:trash',
|
||||
show: this.showDelete,
|
||||
helpModeKey: 'delete_file',
|
||||
},
|
||||
{
|
||||
id: 'assign-btn',
|
||||
@ -120,6 +121,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: this.assignTooltip,
|
||||
icon: 'red:assign',
|
||||
show: this.showAssign,
|
||||
helpModeKey: 'assign_user',
|
||||
},
|
||||
{
|
||||
id: 'assign-to-me-btn',
|
||||
@ -128,6 +130,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.assign-me'),
|
||||
icon: 'red:assign-me',
|
||||
show: this.showAssignToSelf,
|
||||
helpModeKey: 'assign_user',
|
||||
},
|
||||
{
|
||||
id: 'open-import-redactions-dialog-btn',
|
||||
@ -145,6 +148,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltipClass: 'small',
|
||||
show: this._permissionsService.canDownloadRedactedFile() && !!this.file.lastProcessed,
|
||||
disabled: this.file.processingStatus === ProcessingFileStatuses.ERROR,
|
||||
helpModeKey: 'download',
|
||||
},
|
||||
{
|
||||
id: 'toggle-document-info-btn',
|
||||
@ -154,6 +158,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
ariaExpanded: toObservable(this._documentInfoService?.shown, { injector: this._injector }),
|
||||
icon: 'red:status-info',
|
||||
show: !!this._documentInfoService,
|
||||
helpModeKey: 'document_info',
|
||||
},
|
||||
{
|
||||
id: 'toggle-exclude-pages-btn',
|
||||
@ -172,6 +177,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.back-to-new'),
|
||||
icon: 'red:undo',
|
||||
show: this.showSetToNew,
|
||||
helpModeKey: 'change_status',
|
||||
},
|
||||
{
|
||||
id: 'set-file-under-approval-btn',
|
||||
@ -180,6 +186,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.under-approval'),
|
||||
icon: 'red:ready-for-approval',
|
||||
show: this.showUnderApproval,
|
||||
helpModeKey: 'change_status',
|
||||
},
|
||||
{
|
||||
id: 'set-file-under-review-btn',
|
||||
@ -188,6 +195,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.under-review'),
|
||||
icon: 'red:undo',
|
||||
show: this.showUnderReview,
|
||||
helpModeKey: 'change_status',
|
||||
},
|
||||
{
|
||||
id: 'set-file-approved-btn',
|
||||
@ -197,6 +205,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
icon: 'red:approved',
|
||||
disabled: !this.file.canBeApproved,
|
||||
show: this.showApprove,
|
||||
helpModeKey: 'change_status',
|
||||
},
|
||||
{
|
||||
id: 'toggle-automatic-analysis-btn',
|
||||
@ -205,6 +214,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.stop-auto-analysis'),
|
||||
icon: 'red:disable-analysis',
|
||||
show: this.canDisableAutoAnalysis,
|
||||
helpModeKey: 'stop_analysis',
|
||||
},
|
||||
{
|
||||
id: 'reanalyse-file-preview-btn',
|
||||
@ -215,6 +225,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
icon: 'iqser:refresh',
|
||||
show: this.showReanalyseFilePreview,
|
||||
disabled: this.file.isProcessing,
|
||||
helpModeKey: 'stop_analysis',
|
||||
},
|
||||
{
|
||||
id: 'toggle-automatic-analysis-btn',
|
||||
@ -224,6 +235,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
buttonType: this.isFilePreview ? CircleButtonTypes.warn : CircleButtonTypes.default,
|
||||
icon: 'red:enable-analysis',
|
||||
show: this.canEnableAutoAnalysis,
|
||||
helpModeKey: 'stop_analysis',
|
||||
},
|
||||
{
|
||||
id: 'set-under-approval-btn',
|
||||
@ -232,6 +244,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.under-approval'),
|
||||
icon: 'red:undo',
|
||||
show: this.showUndoApproval,
|
||||
helpModeKey: 'change_status',
|
||||
},
|
||||
{
|
||||
id: 'ocr-file-btn',
|
||||
@ -248,6 +261,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
tooltip: _('dossier-overview.reanalyse.action'),
|
||||
icon: 'iqser:refresh',
|
||||
show: this.showReanalyseDossierOverview,
|
||||
helpModeKey: 'stop_analysis',
|
||||
},
|
||||
{
|
||||
id: 'toggle-analysis-btn',
|
||||
@ -258,6 +272,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
class: { 'mr-24': this.isDossierOverviewList },
|
||||
checked: !this.file.excluded,
|
||||
show: this.showToggleAnalysis,
|
||||
helpModeKey: 'disable_extraction',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<button
|
||||
[routerLinkActive]="'active'"
|
||||
[routerLink]="['..', DOSSIERS_ROUTE]"
|
||||
[attr.help-mode-key]="'dossier_list'"
|
||||
[attr.help-mode-key]="'active_archived_dossiers'"
|
||||
class="red-tab"
|
||||
id="active-button"
|
||||
>
|
||||
@ -13,7 +13,7 @@
|
||||
[disabled]="dossierTemplate.numberOfArchivedDossiers === 0"
|
||||
[routerLinkActive]="'active'"
|
||||
[routerLink]="['..', ARCHIVE_ROUTE]"
|
||||
[attr.help-mode-key]="'dossier_list'"
|
||||
[attr.help-mode-key]="'active_archived_dossiers'"
|
||||
class="red-tab"
|
||||
id="archived-button"
|
||||
>
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="btn.tooltip | translate"
|
||||
[type]="btn.buttonType || buttonType"
|
||||
[attr.help-mode-key]="helpModeKey(btn)"
|
||||
></iqser-circle-button>
|
||||
|
||||
<!-- download redacted file-->
|
||||
@ -23,6 +24,7 @@
|
||||
[tooltipClass]="btn.tooltipClass"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[type]="buttonType"
|
||||
[attr.help-mode-key]="helpModeKey(btn)"
|
||||
></redaction-file-download-btn>
|
||||
|
||||
<!-- exclude from redaction -->
|
||||
@ -35,6 +37,7 @@
|
||||
[matTooltipPosition]="tooltipPosition"
|
||||
[matTooltip]="btn.tooltip | translate"
|
||||
[ngClass]="btn.class"
|
||||
[attr.help-mode-key]="helpModeKey(btn)"
|
||||
color="primary"
|
||||
iqserStopPropagation
|
||||
></mat-slide-toggle>
|
||||
|
||||
@ -20,6 +20,8 @@ export class ExpandableFileActionsComponent implements OnChanges {
|
||||
@Input() minWidth: number;
|
||||
@Input() buttonType: CircleButtonType;
|
||||
@Input() tooltipPosition: IqserTooltipPosition;
|
||||
@Input() helpModeKeyPrefix: 'dossier' | 'editor';
|
||||
@Input() isDossierOverviewWorkflow = false;
|
||||
|
||||
displayedButtons: Action[];
|
||||
hiddenButtons: Action[];
|
||||
@ -73,6 +75,12 @@ export class ExpandableFileActionsComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
helpModeKey(action: Action) {
|
||||
return action.helpModeKey
|
||||
? `${this.helpModeKeyPrefix}${this.isDossierOverviewWorkflow ? '_workflow' : ''}_${action.helpModeKey}`
|
||||
: '';
|
||||
}
|
||||
|
||||
onButtonClick(button: Action, $event: MouseEvent) {
|
||||
button.action($event);
|
||||
this.matMenu.closeMenu();
|
||||
|
||||
@ -25,7 +25,6 @@ const NOTIFICATIONS_THRESHOLD = 1000;
|
||||
export class NotificationsService extends EntitiesService<INotification, Notification> implements OnDestroy {
|
||||
readonly #config = getConfig<AppConfig>();
|
||||
readonly #subscription = new Subscription();
|
||||
readonly #filterDocumineNotifications: string[] = [NotificationTypes.ASSIGN_REVIEWER];
|
||||
protected readonly _defaultModelPath = 'notification';
|
||||
protected readonly _entityClass = Notification;
|
||||
|
||||
@ -119,10 +118,6 @@ export class NotificationsService extends EntitiesService<INotification, Notific
|
||||
const todayDate = dayjs(new Date());
|
||||
|
||||
notifications = notifications.filter(n => {
|
||||
if (this.#config.IS_DOCUMINE && this.#filterDocumineNotifications.includes(n.notificationType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(n.notificationType in NotificationTypes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
|
||||
export interface DialogOption {
|
||||
label: string;
|
||||
labelBulk?: string;
|
||||
description: string;
|
||||
descriptionBulk?: string;
|
||||
extraOptionLabel?: string;
|
||||
}
|
||||
|
||||
|
||||
@ -6,17 +6,22 @@ export const removeAnnotationTranslations: { [key in RemoveAnnotationOption]: Di
|
||||
ONLY_HERE: {
|
||||
label: _('remove-annotation.dialog.content.options.only-here.label'),
|
||||
description: _('remove-annotation.dialog.content.options.only-here.description'),
|
||||
descriptionBulk: _('remove-annotation.dialog.content.options.only-here.description-bulk'),
|
||||
},
|
||||
IN_DOSSIER: {
|
||||
label: _('remove-annotation.dialog.content.options.in-dossier.label'),
|
||||
labelBulk: _('remove-annotation.dialog.content.options.in-dossier.label-bulk'),
|
||||
description: _('remove-annotation.dialog.content.options.in-dossier.description'),
|
||||
descriptionBulk: _('remove-annotation.dialog.content.options.in-dossier.description-bulk'),
|
||||
},
|
||||
FALSE_POSITIVE: {
|
||||
label: _('remove-annotation.dialog.content.options.false-positive.label'),
|
||||
description: _('remove-annotation.dialog.content.options.false-positive.description'),
|
||||
descriptionBulk: _('remove-annotation.dialog.content.options.false-positive.description-bulk'),
|
||||
},
|
||||
DO_NOT_RECOMMEND: {
|
||||
label: _('remove-redaction.dialog.content.options.do-not-recommend.label'),
|
||||
description: _('remove-redaction.dialog.content.options.do-not-recommend.description'),
|
||||
descriptionBulk: _('remove-redaction.dialog.content.options.do-not-recommend.description-bulk'),
|
||||
},
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dan.iqser.cloud",
|
||||
"APP_NAME": "RedactManager",
|
||||
"IS_DOCUMINE": false,
|
||||
"IS_DOCUMINE": true,
|
||||
"RULE_EDITOR_DEV_ONLY": false,
|
||||
"AUTO_READ_TIME": 3,
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
@ -18,8 +18,8 @@
|
||||
"SELECTION_MODE": "structural",
|
||||
"MANUAL_BASE_URL": "https://docs.redactmanager.com/preview",
|
||||
"ANNOTATIONS_THRESHOLD": 1000,
|
||||
"THEME": "redact",
|
||||
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/redact/",
|
||||
"THEME": "scm",
|
||||
"BASE_TRANSLATIONS_DIRECTORY": "/assets/i18n/scm/",
|
||||
"AVAILABLE_NOTIFICATIONS_DAYS": 30,
|
||||
"AVAILABLE_OLD_NOTIFICATIONS_MINUTES": 60,
|
||||
"NOTIFICATIONS_THRESHOLD": 1000,
|
||||
|
||||
@ -27,14 +27,19 @@
|
||||
"documentKey": "filter_dossier_list"
|
||||
},
|
||||
{
|
||||
"elementKey": "navigate_in_breadcrumbs",
|
||||
"documentKey": "navigate_in_breadcrumbs"
|
||||
"elementKey": "navigate_breadcrumbs",
|
||||
"documentKey": "navigate_breadcrumbs"
|
||||
},
|
||||
{
|
||||
"elementKey": "new_dossier_button",
|
||||
"documentKey": "new_dossier_button",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "new_dossier",
|
||||
"documentKey": "new_dossier",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "open_notifications",
|
||||
"documentKey": "open_notifications"
|
||||
@ -48,13 +53,8 @@
|
||||
"documentKey": "views"
|
||||
},
|
||||
{
|
||||
"elementKey": "search_in_entire_application",
|
||||
"documentKey": "search_in_entire_application"
|
||||
},
|
||||
{
|
||||
"elementKey": "edit_dossier_in_dossier",
|
||||
"documentKey": "edit_dossier_in_dossier",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
"elementKey": "search_entire_application",
|
||||
"documentKey": "search_entire_application"
|
||||
},
|
||||
{
|
||||
"elementKey": "document_features_in_editor",
|
||||
@ -155,8 +155,20 @@
|
||||
"overlappingElements": ["USER_MENU", "DOCUMENT_INFO"]
|
||||
},
|
||||
{
|
||||
"elementKey": "user_account",
|
||||
"documentKey": "user_account"
|
||||
"elementKey": "my_profile",
|
||||
"documentKey": "my_profile"
|
||||
},
|
||||
{
|
||||
"elementKey": "notification_preferences",
|
||||
"documentKey": "notification_preferences"
|
||||
},
|
||||
{
|
||||
"elementKey": "user_preferences",
|
||||
"documentKey": "user_preferences"
|
||||
},
|
||||
{
|
||||
"elementKey": "prompts_and_dialogs",
|
||||
"documentKey": "prompts_and_dialogs"
|
||||
},
|
||||
{
|
||||
"elementKey": "my_downloads",
|
||||
@ -167,8 +179,8 @@
|
||||
"documentKey": "trash"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_overview",
|
||||
"documentKey": "dossier_overview"
|
||||
"elementKey": "open_dossier_template",
|
||||
"documentKey": "open_dossier_template"
|
||||
},
|
||||
{
|
||||
"elementKey": "home",
|
||||
@ -189,7 +201,8 @@
|
||||
{
|
||||
"elementKey": "dossier",
|
||||
"documentKey": "dossier",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
"scrollableParentView": "VIRTUAL_SCROLL",
|
||||
"overlappingElements": ["BREADCRUMBS_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "document_in_editor",
|
||||
@ -198,7 +211,8 @@
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_list",
|
||||
"documentKey": "dossier_list"
|
||||
"documentKey": "dossier_list",
|
||||
"overlappingElements": ["BREADCRUMBS_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_templates",
|
||||
@ -209,6 +223,10 @@
|
||||
"documentKey": "edit_clone_delete_dossier_templates",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "user_dossier_template_info",
|
||||
"documentKey": "user_dossier_template_info"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_templates_info",
|
||||
"documentKey": "dossier_templates_info"
|
||||
@ -217,6 +235,10 @@
|
||||
"elementKey": "entities",
|
||||
"documentKey": "entities"
|
||||
},
|
||||
{
|
||||
"elementKey": "user_dossier_template_entities",
|
||||
"documentKey": "user_dossier_template_entities"
|
||||
},
|
||||
{
|
||||
"elementKey": "edit_delete_entities",
|
||||
"documentKey": "edit_delete_entities",
|
||||
@ -279,6 +301,10 @@
|
||||
"documentKey": "edit_delete_dossier_attributes",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "user_dossier_template_dossier_states",
|
||||
"documentKey": "user_dossier_template_dossier_states"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_states",
|
||||
"documentKey": "dossier_states"
|
||||
@ -296,6 +322,14 @@
|
||||
"documentKey": "reports",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "user_dossier_template_reports",
|
||||
"documentKey": "user_dossier_template_reports"
|
||||
},
|
||||
{
|
||||
"elementKey": "admin_reports",
|
||||
"documentKey": "reports"
|
||||
},
|
||||
{
|
||||
"elementKey": "justifications",
|
||||
"documentKey": "justifications"
|
||||
@ -375,5 +409,192 @@
|
||||
{
|
||||
"elementKey": "edit-file-attributes",
|
||||
"documentKey": "document_list"
|
||||
},
|
||||
{
|
||||
"elementKey": "active_archived_dossiers",
|
||||
"documentKey": "active_archived_dossiers"
|
||||
},
|
||||
{
|
||||
"elementKey": "quick_filter_dossiers",
|
||||
"documentKey": "quick_filter_dossiers"
|
||||
},
|
||||
{
|
||||
"elementKey": "edit_dossier",
|
||||
"documentKey": "edit_dossier"
|
||||
},
|
||||
{
|
||||
"elementKey": "download_dossier_in_dossier",
|
||||
"documentKey": "download_dossier",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "download_csv",
|
||||
"documentKey": "download_csv",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "upload_document",
|
||||
"documentKey": "upload_document",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "close_dossier",
|
||||
"documentKey": "close_dossier",
|
||||
"overlappingElements": ["USER_MENU"]
|
||||
},
|
||||
{
|
||||
"elementKey": "quick_filters_documents",
|
||||
"documentKey": "quick_filters_documents"
|
||||
},
|
||||
{
|
||||
"elementKey": "filter_documents",
|
||||
"documentKey": "filter_documents"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_delete_file",
|
||||
"documentKey": "dossier_delete_file",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_assign_user",
|
||||
"documentKey": "dossier_assign_user",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_change_status",
|
||||
"documentKey": "dossier_change_status",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_stop_analysis",
|
||||
"documentKey": "dossier_stop_analysis",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_download",
|
||||
"documentKey": "dossier_download",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_disable_extraction",
|
||||
"documentKey": "dossier_disable_extraction",
|
||||
"scrollableParentView": "VIRTUAL_SCROLL"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_delete_file",
|
||||
"documentKey": "dossier_delete_file",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_assign_user",
|
||||
"documentKey": "dossier_assign_user",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_change_status",
|
||||
"documentKey": "dossier_change_status",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_stop_analysis",
|
||||
"documentKey": "dossier_stop_analysis",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_download",
|
||||
"documentKey": "dossier_download",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "dossier_workflow_disable_extraction",
|
||||
"documentKey": "dossier_disable_extraction",
|
||||
"scrollableParentView": "WORKFLOW_VIEW"
|
||||
},
|
||||
{
|
||||
"elementKey": "workload_page_list",
|
||||
"documentKey": "workload_page_list"
|
||||
},
|
||||
|
||||
{
|
||||
"elementKey": "editor_delete_file",
|
||||
"documentKey": "editor_delete_file"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_assign_user",
|
||||
"documentKey": "editor_assign_user"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_change_status",
|
||||
"documentKey": "editor_change_status"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_stop_analysis",
|
||||
"documentKey": "editor_stop_analysis"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_download",
|
||||
"documentKey": "editor_download"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_disable_extraction",
|
||||
"documentKey": "editor_disable_extraction"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_scm",
|
||||
"documentKey": "editor_scm"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_document_info",
|
||||
"documentKey": "editor_document_info"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_full_screen",
|
||||
"documentKey": "editor_full_screen"
|
||||
},
|
||||
{
|
||||
"elementKey": "editor_close",
|
||||
"documentKey": "editor_close"
|
||||
},
|
||||
{
|
||||
"elementKey": "scm_edit_DIALOG",
|
||||
"documentKey": "scm_edit",
|
||||
"scrollableParentView": "SCM_EDIT_DIALOG"
|
||||
},
|
||||
{
|
||||
"elementKey": "scm_undo_DIALOG",
|
||||
"documentKey": "scm_undo",
|
||||
"scrollableParentView": "SCM_EDIT_DIALOG"
|
||||
},
|
||||
{
|
||||
"elementKey": "scm_export_DIALOG",
|
||||
"documentKey": "scm_export"
|
||||
},
|
||||
{
|
||||
"elementKey": "resize_annotation",
|
||||
"documentKey": "resize_annotation",
|
||||
"scrollableParentView": "ANNOTATIONS_LIST",
|
||||
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
|
||||
},
|
||||
{
|
||||
"elementKey": "edit_annotation",
|
||||
"documentKey": "edit_annotation",
|
||||
"scrollableParentView": "ANNOTATIONS_LIST",
|
||||
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
|
||||
},
|
||||
{
|
||||
"elementKey": "remove_annotation",
|
||||
"documentKey": "remove_annotation",
|
||||
"scrollableParentView": "ANNOTATIONS_LIST",
|
||||
"overlappingElements": ["USER_MENU", "WORKLOAD_FILTER", "DOCUMENT_INFO"]
|
||||
},
|
||||
{
|
||||
"elementKey": "workload_filter",
|
||||
"documentKey": "workload_filter",
|
||||
"overlappingElements": ["USER_MENU", "DOCUMENT_INFO"]
|
||||
},
|
||||
{
|
||||
"elementKey": "workload_bulk_selection",
|
||||
"documentKey": "workload_bulk_selection",
|
||||
"overlappingElements": ["USER_MENU", "DOCUMENT_INFO"]
|
||||
}
|
||||
]
|
||||
|
||||
@ -234,6 +234,7 @@
|
||||
},
|
||||
"admin-side-nav": {
|
||||
"audit": "Audit",
|
||||
"component-rule-editor": "",
|
||||
"configurations": "Configurations",
|
||||
"default-colors": "Default Colors",
|
||||
"dictionary": "Dictionary",
|
||||
@ -557,6 +558,19 @@
|
||||
"title": "Aktion bestätigen"
|
||||
}
|
||||
},
|
||||
"component-rules-screen": {
|
||||
"error": {
|
||||
"generic": ""
|
||||
},
|
||||
"errors-found": "",
|
||||
"revert-changes": "",
|
||||
"save-changes": "",
|
||||
"success": {
|
||||
"generic": ""
|
||||
},
|
||||
"title": "",
|
||||
"warning-text": ""
|
||||
},
|
||||
"configurations": "Einstellungen",
|
||||
"confirm-archive-dossier": {
|
||||
"archive": "Archive Dossier",
|
||||
@ -1237,7 +1251,8 @@
|
||||
"reason": "Reason",
|
||||
"redacted-text": "Redacted text",
|
||||
"section": "Paragraph / Location",
|
||||
"type": "Type"
|
||||
"type": "Type",
|
||||
"unchanged": ""
|
||||
},
|
||||
"title": "Edit {type, select, image{Image} hint{Hint} other{Redaction}}"
|
||||
}
|
||||
@ -2019,22 +2034,29 @@
|
||||
"save": "Save"
|
||||
},
|
||||
"content": {
|
||||
"comment": "Comment",
|
||||
"comment-placeholder": "Add remarks or mentions ...",
|
||||
"comment": "",
|
||||
"comment-placeholder": "",
|
||||
"list-item": "",
|
||||
"list-item-false-positive": "",
|
||||
"options": {
|
||||
"false-positive": {
|
||||
"description": "\"{value}\" is not a \"{type}\" in this context: \"{context}\".",
|
||||
"label": "False positive"
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"label": ""
|
||||
},
|
||||
"in-dossier": {
|
||||
"description": "Do not annotate \"{value}\" as \"{type}\" in any dossier.",
|
||||
"label": "No longer annotate as \"{type}\""
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"label": "",
|
||||
"label-bulk": ""
|
||||
},
|
||||
"only-here": {
|
||||
"description": "Do not annotate \"{value}\" at this position in the current document.",
|
||||
"label": "Remove here"
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"redacted-text": ""
|
||||
},
|
||||
"title": "Remove annotation"
|
||||
}
|
||||
@ -2050,9 +2072,10 @@
|
||||
"comment-placeholder": "Add remarks or mentions ...",
|
||||
"options": {
|
||||
"do-not-recommend": {
|
||||
"description": "Do not recommend \"{value}\" as {type} in any document of the current dossier.",
|
||||
"extraOptionLabel": "Apply to all dossiers",
|
||||
"label": "Remove from dossier"
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"extraOptionLabel": "",
|
||||
"label": ""
|
||||
},
|
||||
"false-positive": {
|
||||
"description": "\"{value}\" is not a {type} in this context: \"{context}\".",
|
||||
@ -2212,6 +2235,7 @@
|
||||
"error": {
|
||||
"generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!"
|
||||
},
|
||||
"errors-found": "",
|
||||
"revert-changes": "Anmeldedaten speichern",
|
||||
"save-changes": "Änderungen speichern",
|
||||
"success": {
|
||||
|
||||
@ -234,6 +234,7 @@
|
||||
},
|
||||
"admin-side-nav": {
|
||||
"audit": "Audit",
|
||||
"component-rule-editor": "",
|
||||
"configurations": "Configurations",
|
||||
"default-colors": "Default Colors",
|
||||
"dictionary": "Dictionary",
|
||||
@ -557,6 +558,19 @@
|
||||
"title": "Confirm Action"
|
||||
}
|
||||
},
|
||||
"component-rules-screen": {
|
||||
"error": {
|
||||
"generic": ""
|
||||
},
|
||||
"errors-found": "",
|
||||
"revert-changes": "",
|
||||
"save-changes": "",
|
||||
"success": {
|
||||
"generic": ""
|
||||
},
|
||||
"title": "",
|
||||
"warning-text": ""
|
||||
},
|
||||
"configurations": "Configurations",
|
||||
"confirm-archive-dossier": {
|
||||
"archive": "Archive Dossier",
|
||||
@ -1237,7 +1251,8 @@
|
||||
"reason": "Reason",
|
||||
"redacted-text": "Redacted text",
|
||||
"section": "Paragraph / Location",
|
||||
"type": "Type"
|
||||
"type": "Type",
|
||||
"unchanged": "Unchanged"
|
||||
},
|
||||
"title": "Edit {type, select, image{Image} hint{Hint} other{Redaction}}"
|
||||
}
|
||||
@ -2021,22 +2036,29 @@
|
||||
"content": {
|
||||
"comment": "Comment",
|
||||
"comment-placeholder": "Add remarks or mentions ...",
|
||||
"list-item": "",
|
||||
"list-item-false-positive": "",
|
||||
"options": {
|
||||
"false-positive": {
|
||||
"description": "\"{value}\" is not a \"{type}\" in this context: \"{context}\".",
|
||||
"description-bulk": "",
|
||||
"label": "False positive"
|
||||
},
|
||||
"in-dossier": {
|
||||
"description": "Do not annotate \"{value}\" as \"{type}\" in any dossier.",
|
||||
"label": "No longer annotate as \"{type}\""
|
||||
"description-bulk": "",
|
||||
"label": "No longer annotate as \"{type}\"",
|
||||
"label-bulk": ""
|
||||
},
|
||||
"only-here": {
|
||||
"description": "Do not annotate \"{value}\" at this position in the current document.",
|
||||
"description-bulk": "",
|
||||
"label": "Remove here"
|
||||
}
|
||||
}
|
||||
},
|
||||
"redacted-text": ""
|
||||
},
|
||||
"title": "Remove annotation"
|
||||
"title": "Remove {count, plural, one{annotation} other {annotations}}"
|
||||
}
|
||||
},
|
||||
"remove-redaction": {
|
||||
@ -2051,6 +2073,7 @@
|
||||
"options": {
|
||||
"do-not-recommend": {
|
||||
"description": "Do not recommend \"{value}\" as {type} in any document of the current dossier.",
|
||||
"description-bulk": "",
|
||||
"extraOptionLabel": "Apply to all dossiers",
|
||||
"label": "Remove from dossier"
|
||||
},
|
||||
@ -2209,16 +2232,17 @@
|
||||
"title": "Structured Component Management"
|
||||
},
|
||||
"rules-screen": {
|
||||
"title": "Rule Editor",
|
||||
"warning-text": "Warning: experimental feature!",
|
||||
"error": {
|
||||
"generic": "Something went wrong... Rules update failed!"
|
||||
},
|
||||
"errors-found": "{errors, plural, one{An error} other{{errors} errors}} found in rules",
|
||||
"revert-changes": "Revert",
|
||||
"save-changes": "Save Changes",
|
||||
"success": {
|
||||
"generic": "Rules updated!"
|
||||
}
|
||||
},
|
||||
"title": "Rule Editor",
|
||||
"warning-text": "Warning: experimental feature!"
|
||||
},
|
||||
"search-screen": {
|
||||
"cols": {
|
||||
|
||||
@ -234,6 +234,7 @@
|
||||
},
|
||||
"admin-side-nav": {
|
||||
"audit": "",
|
||||
"component-rule-editor": "",
|
||||
"configurations": "",
|
||||
"default-colors": "",
|
||||
"dictionary": "",
|
||||
@ -557,6 +558,19 @@
|
||||
"title": "Aktion bestätigen"
|
||||
}
|
||||
},
|
||||
"component-rules-screen": {
|
||||
"error": {
|
||||
"generic": ""
|
||||
},
|
||||
"errors-found": "",
|
||||
"revert-changes": "",
|
||||
"save-changes": "",
|
||||
"success": {
|
||||
"generic": ""
|
||||
},
|
||||
"title": "",
|
||||
"warning-text": ""
|
||||
},
|
||||
"configurations": "Einstellungen",
|
||||
"confirm-archive-dossier": {
|
||||
"archive": "",
|
||||
@ -1237,7 +1251,8 @@
|
||||
"reason": "",
|
||||
"redacted-text": "",
|
||||
"section": "",
|
||||
"type": ""
|
||||
"type": "",
|
||||
"unchanged": ""
|
||||
},
|
||||
"title": ""
|
||||
}
|
||||
@ -2021,20 +2036,27 @@
|
||||
"content": {
|
||||
"comment": "",
|
||||
"comment-placeholder": "",
|
||||
"list-item": "",
|
||||
"list-item-false-positive": "",
|
||||
"options": {
|
||||
"false-positive": {
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"label": ""
|
||||
},
|
||||
"in-dossier": {
|
||||
"description": "",
|
||||
"label": ""
|
||||
"description-bulk": "",
|
||||
"label": "",
|
||||
"label-bulk": ""
|
||||
},
|
||||
"only-here": {
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"redacted-text": ""
|
||||
},
|
||||
"title": ""
|
||||
}
|
||||
@ -2051,6 +2073,7 @@
|
||||
"options": {
|
||||
"do-not-recommend": {
|
||||
"description": "",
|
||||
"description-bulk": "",
|
||||
"extraOptionLabel": "",
|
||||
"label": ""
|
||||
},
|
||||
@ -2209,16 +2232,17 @@
|
||||
"title": ""
|
||||
},
|
||||
"rules-screen": {
|
||||
"title": "",
|
||||
"warning-text": "",
|
||||
"error": {
|
||||
"generic": "Es ist ein Fehler aufgetreten ... Die Regeln konnten nicht aktualisiert werden!"
|
||||
},
|
||||
"errors-found": "",
|
||||
"revert-changes": "Anmeldedaten speichern",
|
||||
"save-changes": "Änderungen speichern",
|
||||
"success": {
|
||||
"generic": "Die Regeln wurden aktualisiert!"
|
||||
}
|
||||
},
|
||||
"title": "",
|
||||
"warning-text": ""
|
||||
},
|
||||
"search-screen": {
|
||||
"cols": {
|
||||
|
||||
@ -234,6 +234,7 @@
|
||||
},
|
||||
"admin-side-nav": {
|
||||
"audit": "Audit",
|
||||
"component-rule-editor": "Component Rule Editor",
|
||||
"configurations": "Configurations",
|
||||
"default-colors": "Default Colors",
|
||||
"dictionary": "Dictionary",
|
||||
@ -557,6 +558,19 @@
|
||||
"title": "Confirm Action"
|
||||
}
|
||||
},
|
||||
"component-rules-screen": {
|
||||
"error": {
|
||||
"generic": "Something went wrong... Component rules update failed!"
|
||||
},
|
||||
"errors-found": "{errors, plural, one{An error} other{{errors} errors}} found in rules",
|
||||
"revert-changes": "Revert",
|
||||
"save-changes": "Save Changes",
|
||||
"success": {
|
||||
"generic": "Component rules updated!"
|
||||
},
|
||||
"title": "Component Rule Editor",
|
||||
"warning-text": "Warning: experimental feature!"
|
||||
},
|
||||
"configurations": "Configurations",
|
||||
"confirm-archive-dossier": {
|
||||
"archive": "Archive Dossier",
|
||||
@ -1235,9 +1249,10 @@
|
||||
}
|
||||
},
|
||||
"reason": "",
|
||||
"redacted-text": "Annotated text",
|
||||
"redacted-text": "Selected {length, plural, one{annotation} other {annotations}}",
|
||||
"section": "",
|
||||
"type": "Type"
|
||||
"type": "Type",
|
||||
"unchanged": "Unchanged"
|
||||
},
|
||||
"title": "Edit annotation"
|
||||
}
|
||||
@ -2021,22 +2036,29 @@
|
||||
"content": {
|
||||
"comment": "Comment",
|
||||
"comment-placeholder": "Add remarks or mentions ...",
|
||||
"list-item": "{text}",
|
||||
"list-item-false-positive": "\"{text}\" in the context: \"{context}\"",
|
||||
"options": {
|
||||
"false-positive": {
|
||||
"description": "\"{value}\" is not a \"{type}\" in this context: \"{context}\".",
|
||||
"description-bulk": "The selected items should not be annotated in their respective contexts.",
|
||||
"label": "False positive"
|
||||
},
|
||||
"in-dossier": {
|
||||
"description": "Do not annotate \"{value}\" as \"{type}\" in any dossier.",
|
||||
"label": "No longer annotate as \"{type}\""
|
||||
"description-bulk": "Do not annotate the selected terms as their respective types in any dossier.",
|
||||
"label": "No longer annotate as \"{type}\"",
|
||||
"label-bulk": "No longer annotate in any dossier"
|
||||
},
|
||||
"only-here": {
|
||||
"description": "Do not annotate \"{value}\" at this position in the current document.",
|
||||
"description-bulk": "Do not annotate the selected terms at this position in the current document.",
|
||||
"label": "Remove here"
|
||||
}
|
||||
}
|
||||
},
|
||||
"redacted-text": "Selected annotations"
|
||||
},
|
||||
"title": "Remove annotation"
|
||||
"title": "Remove {count, plural, one{annotation} other {annotations}}"
|
||||
}
|
||||
},
|
||||
"remove-redaction": {
|
||||
@ -2050,9 +2072,10 @@
|
||||
"comment-placeholder": "Add remarks or mentions ...",
|
||||
"options": {
|
||||
"do-not-recommend": {
|
||||
"description": "",
|
||||
"extraOptionLabel": "",
|
||||
"label": ""
|
||||
"description": "Do not recommend \"{value}\" as {type} in any document of the current dossier.",
|
||||
"description-bulk": "Do not recommend the selected values as their respective types in any document of the current dossier.",
|
||||
"extraOptionLabel": "Apply to all dossiers",
|
||||
"label": "Remove from dossier"
|
||||
},
|
||||
"false-positive": {
|
||||
"description": "\"{value}\" is not a {type} in this context: \"{context}\".",
|
||||
@ -2209,16 +2232,17 @@
|
||||
"title": "Component View"
|
||||
},
|
||||
"rules-screen": {
|
||||
"title": "Rule Editor",
|
||||
"warning-text": "Warning: experimental feature!",
|
||||
"error": {
|
||||
"generic": "Something went wrong... Rules update failed!"
|
||||
},
|
||||
"errors-found": "{errors, plural, one{An error} other{{errors} errors}} found in rules",
|
||||
"revert-changes": "Revert",
|
||||
"save-changes": "Save Changes",
|
||||
"success": {
|
||||
"generic": "Rules updated!"
|
||||
}
|
||||
},
|
||||
"title": "Rule Editor",
|
||||
"warning-text": "Warning: experimental feature!"
|
||||
},
|
||||
"search-screen": {
|
||||
"cols": {
|
||||
|
||||
@ -4,6 +4,7 @@ body.light {
|
||||
--iqser-primary-2: #4d97dd;
|
||||
--iqser-red-1: #4d4fdd;
|
||||
--iqser-red-2: #4d97dd;
|
||||
--iqser-helpmode-primary: #fdbd00;
|
||||
/*Override the default light theme here*/
|
||||
/*Copy CSS variables from theme-template.css file*/
|
||||
}
|
||||
@ -14,6 +15,7 @@ body.dark {
|
||||
--iqser-primary-2: #4d97dd;
|
||||
--iqser-red-1: #4d4fdd;
|
||||
--iqser-red-2: #4d97dd;
|
||||
--iqser-helpmode-primary: #fdbd00;
|
||||
/*Override the default dark theme here*/
|
||||
/*Copy CSS variables from theme-template.css file*/
|
||||
}
|
||||
|
||||
13
libs/red-domain/src/lib/shared/component-rules.ts
Normal file
13
libs/red-domain/src/lib/shared/component-rules.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Object containing a string of Drools rules.
|
||||
*/
|
||||
export interface IComponentRules {
|
||||
/**
|
||||
* The DossierTemplate Id for these rules
|
||||
*/
|
||||
dossierTemplateId?: string;
|
||||
/**
|
||||
* The actual string of rules.
|
||||
*/
|
||||
rules?: string;
|
||||
}
|
||||
@ -11,3 +11,4 @@ export * from './admin-side-nav-types';
|
||||
export * from './charts';
|
||||
export * from './app-config';
|
||||
export * from './system-preferences';
|
||||
export * from './component-rules';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user