Pull request #84: Rule sets

Merge in RED/ui from rule-sets to master

* commit '615a45d91d78959d02f22baa61171885c4105f1c':
  Rule sets
This commit is contained in:
Timo Bejan 2021-01-08 10:01:45 +01:00
commit 4e4f99f0fb
31 changed files with 288 additions and 226 deletions

View File

@ -218,6 +218,15 @@ const routes = [
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
} }
}, },
{
path: 'watermark',
component: WatermarkScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
},
{ path: '', redirectTo: 'dictionaries', pathMatch: 'full' } { path: '', redirectTo: 'dictionaries', pathMatch: 'full' }
] ]
} }
@ -230,15 +239,6 @@ const routes = [
data: { data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard] routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard]
} }
},
{
path: 'watermark',
component: WatermarkScreenComponent,
canActivate: [CompositeRouteGuard],
data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard],
requiredRoles: ['RED_ADMIN']
}
} }
] ]
} }

View File

@ -16,3 +16,7 @@
margin-right: 5px; margin-right: 5px;
font-weight: bold; font-weight: bold;
} }
redaction-status-bar {
margin-left: 2px;
}

View File

@ -43,10 +43,11 @@ export class PermissionsService {
if (!fileStatus) { if (!fileStatus) {
return false; return false;
} }
const project = this._appStateService.getProjectById(fileStatus.projectId);
return ( return (
((fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') && ((fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') &&
(fileStatus.dictionaryVersion !== this._appStateService.dictionaryVersion || (fileStatus.dictionaryVersion !== this._appStateService.dictionaryVersion(project.ruleSetId) ||
fileStatus.rulesVersion !== this._appStateService.rulesVersion || fileStatus.rulesVersion !== this._appStateService.rulesVersion(project.ruleSetId) ||
fileStatus.hasUnappliedSuggestions)) || fileStatus.hasUnappliedSuggestions)) ||
fileStatus.isError fileStatus.isError
); );

View File

@ -5,7 +5,7 @@
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="active" routerLinkActive="active"
translate="project-templates" translate="project-templates"
*ngIf="root || !!ruleSet" *ngIf="root || !!appStateService.activeRuleSetId"
></a> ></a>
<a <a
@ -17,30 +17,25 @@
*ngIf="root && userPreferenceService.areDevFeaturesEnabled" *ngIf="root && userPreferenceService.areDevFeaturesEnabled"
></a> ></a>
<a <ng-container *ngIf="appStateService.activeRuleSetId">
class="breadcrumb"
[routerLink]="'/ui/admin/watermark'"
[routerLinkActiveOptions]="{ exact: true }"
routerLinkActive="active"
translate="watermark"
*ngIf="root && permissionService.isAdmin()"
></a>
<ng-container *ngIf="ruleSet">
<mat-icon svgIcon="red:arrow-right"></mat-icon>
<a class="breadcrumb ml-0" [routerLink]="'/ui/admin/project-templates/' + ruleSet.ruleSetId" [class.active]="!dictionary">
{{ ruleSet.name }}
</a>
</ng-container>
<ng-container *ngIf="dictionary">
<mat-icon svgIcon="red:arrow-right"></mat-icon> <mat-icon svgIcon="red:arrow-right"></mat-icon>
<a <a
class="breadcrumb ml-0" class="breadcrumb ml-0"
[routerLink]="'/ui/admin/project-templates/' + ruleSet.ruleSetId + '/dictionaries/' + dictionary.type" [routerLink]="'/ui/admin/project-templates/' + appStateService.activeRuleSetId"
[class.active]="!appStateService.activeDictionaryType"
>
{{ appStateService.activeRuleSet.name }}
</a>
</ng-container>
<ng-container *ngIf="appStateService.activeDictionaryType">
<mat-icon svgIcon="red:arrow-right"></mat-icon>
<a
class="breadcrumb ml-0"
[routerLink]="'/ui/admin/project-templates/' + appStateService.activeRuleSetId + '/dictionaries/' + appStateService.activeDictionaryType"
routerLinkActive="active" routerLinkActive="active"
> >
{{ dictionary.label }} {{ appStateService.activeDictionary.label }}
</a> </a>
</ng-container> </ng-container>
</div> </div>

View File

@ -1,6 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { RuleSetModel, TypeValue } from '@redaction/red-ui-http';
import { AppStateService } from '../../state/app-state.service'; import { AppStateService } from '../../state/app-state.service';
import { UserPreferenceService } from '../../common/service/user-preference.service'; import { UserPreferenceService } from '../../common/service/user-preference.service';
import { PermissionsService } from '../../common/service/permissions.service'; import { PermissionsService } from '../../common/service/permissions.service';
@ -10,27 +8,12 @@ import { PermissionsService } from '../../common/service/permissions.service';
templateUrl: './admin-breadcrumbs.component.html', templateUrl: './admin-breadcrumbs.component.html',
styleUrls: ['./admin-breadcrumbs.component.scss'] styleUrls: ['./admin-breadcrumbs.component.scss']
}) })
export class AdminBreadcrumbsComponent implements OnInit { export class AdminBreadcrumbsComponent {
public dictionary: TypeValue; @Input() public root = false;
public ruleSet: RuleSetModel;
@Input()
public root = false;
constructor( constructor(
public readonly userPreferenceService: UserPreferenceService, public readonly userPreferenceService: UserPreferenceService,
public readonly permissionService: PermissionsService, public readonly permissionService: PermissionsService,
private readonly _activatedRoute: ActivatedRoute, public readonly appStateService: AppStateService
private readonly _appStateService: AppStateService
) {} ) {}
ngOnInit(): void {
this._activatedRoute.params.subscribe((params) => {
if (params.ruleSetId) {
this.ruleSet = this._appStateService.getRuleSetById(params.ruleSetId);
}
if (params.type) {
this.dictionary = this._appStateService.getDictionaryTypeValue(params.type);
}
});
}
} }

View File

@ -9,6 +9,7 @@ import { TypeValue } from '@redaction/red-ui-http';
}) })
export class DictionaryAnnotationIconComponent implements OnChanges { export class DictionaryAnnotationIconComponent implements OnChanges {
@Input() dictionaryKey: string; @Input() dictionaryKey: string;
@Input() ruleSetId: string;
color: string; color: string;
label: string; label: string;
@ -18,8 +19,8 @@ export class DictionaryAnnotationIconComponent implements OnChanges {
ngOnChanges(): void { ngOnChanges(): void {
if (this.dictionaryKey) { if (this.dictionaryKey) {
const typeValue: TypeValue = this._appStateService.getDictionaryTypeValue(this.dictionaryKey); const typeValue: TypeValue = this._appStateService.getDictionaryTypeValue(this.dictionaryKey, this.ruleSetId);
this.color = this._appStateService.getDictionaryColor(this.dictionaryKey); this.color = this._appStateService.getDictionaryColor(this.dictionaryKey, this.ruleSetId);
this.type = typeValue.hint ? 'circle' : 'square'; this.type = typeValue.hint ? 'circle' : 'square';
this.label = this.dictionaryKey[0].toUpperCase(); this.label = this.dictionaryKey[0].toUpperCase();
} }

View File

@ -25,6 +25,11 @@ export class NeedsWorkBadgeComponent implements OnInit {
} }
get suggestionColor() { get suggestionColor() {
return this.appStateService.getDictionaryColor('suggestion'); let ruleSetId = null;
if (this.needsWorkInput instanceof ProjectWrapper) {
ruleSetId = this.needsWorkInput.ruleSetId;
}
return this.appStateService.getDictionaryColor('suggestion', ruleSetId);
} }
} }

View File

@ -2,6 +2,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { DialogService } from '../../dialogs/dialog.service'; import { DialogService } from '../../dialogs/dialog.service';
import { PermissionsService } from '../../common/service/permissions.service'; import { PermissionsService } from '../../common/service/permissions.service';
import { RuleSetModel } from '@redaction/red-ui-http'; import { RuleSetModel } from '@redaction/red-ui-http';
import { AppStateService } from '../../state/app-state.service';
@Component({ @Component({
selector: 'redaction-rule-set-actions', selector: 'redaction-rule-set-actions',
@ -12,7 +13,15 @@ export class RuleSetActionsComponent implements OnInit {
@Input() ruleSet: RuleSetModel; @Input() ruleSet: RuleSetModel;
@Output() loadRuleSetsData = new EventEmitter<any>(); @Output() loadRuleSetsData = new EventEmitter<any>();
constructor(private readonly _dialogService: DialogService, public readonly permissionsService: PermissionsService) {} constructor(
private readonly _dialogService: DialogService,
private readonly _appStateService: AppStateService,
public readonly permissionsService: PermissionsService
) {
if (!this.ruleSet) {
this.ruleSet = this._appStateService.activeRuleSet;
}
}
ngOnInit(): void {} ngOnInit(): void {}

View File

@ -3,6 +3,7 @@
<mat-button-toggle-group [value]="screen" (change)="switchView($event)" appearance="legacy"> <mat-button-toggle-group [value]="screen" (change)="switchView($event)" appearance="legacy">
<mat-button-toggle [value]="'dictionaries'"> {{ 'dictionaries' | translate }}</mat-button-toggle> <mat-button-toggle [value]="'dictionaries'"> {{ 'dictionaries' | translate }}</mat-button-toggle>
<mat-button-toggle [value]="'rules'"> {{ 'rule-editor' | translate }}</mat-button-toggle> <mat-button-toggle [value]="'rules'"> {{ 'rule-editor' | translate }}</mat-button-toggle>
<mat-button-toggle [value]="'watermark'"> {{ 'watermark' | translate }}</mat-button-toggle>
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router'; import { Router } from '@angular/router';
import { AppStateService } from '../../state/app-state.service';
@Component({ @Component({
selector: 'redaction-rule-set-view-switch', selector: 'redaction-rule-set-view-switch',
@ -7,21 +8,13 @@ import { ActivatedRoute, Router } from '@angular/router';
styleUrls: ['./rule-set-view-switch.component.scss'] styleUrls: ['./rule-set-view-switch.component.scss']
}) })
export class RuleSetViewSwitchComponent implements OnInit { export class RuleSetViewSwitchComponent implements OnInit {
@Input() public screen: 'rules' | 'dictionaries'; @Input() public screen: 'rules' | 'dictionaries' | 'watermark';
private readonly _ruleSetId: string; constructor(private readonly _router: Router, private readonly _appStateService: AppStateService) {}
constructor(private readonly _actr: ActivatedRoute, private _router: Router) {
this._ruleSetId = this._actr.snapshot.params.ruleSetId;
}
ngOnInit(): void {} ngOnInit(): void {}
public switchView($event) { public switchView($event) {
if ($event.value === 'dictionaries') { this._router.navigate(['ui/admin/project-templates/' + this._appStateService.activeRuleSetId + '/' + $event.value]);
this._router.navigate(['ui/admin/project-templates/' + this._ruleSetId + '/dictionaries']);
} else {
this._router.navigate(['ui/admin/project-templates/' + this._ruleSetId + '/rules']);
}
} }
} }

View File

@ -1,6 +1,7 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FilterModel } from '../../common/filter/model/filter.model'; import { FilterModel } from '../../common/filter/model/filter.model';
import { AppStateService } from '../../state/app-state.service'; import { AppStateService } from '../../state/app-state.service';
import { DEFAULT_RUL_SET_UUID } from '../../utils/rule-set-default';
@Component({ @Component({
selector: 'redaction-type-filter', selector: 'redaction-type-filter',
@ -15,6 +16,6 @@ export class TypeFilterComponent implements OnInit {
constructor(public appStateService: AppStateService) {} constructor(public appStateService: AppStateService) {}
ngOnInit(): void { ngOnInit(): void {
this.dictionaryColor = this.appStateService.getDictionaryColor(this.filter.key); this.dictionaryColor = this.appStateService.getDictionaryColor(this.filter.key, this.appStateService.activeProject?.ruleSetId || DEFAULT_RUL_SET_UUID);
} }
} }

View File

@ -5,6 +5,7 @@ import {
FileManagementControllerService, FileManagementControllerService,
FileStatus, FileStatus,
ManualRedactionControllerService, ManualRedactionControllerService,
RuleSetControllerService,
RuleSetModel, RuleSetModel,
TypeValue TypeValue
} from '@redaction/red-ui-http'; } from '@redaction/red-ui-http';
@ -21,7 +22,6 @@ import { ManualAnnotationService } from '../screens/file/service/manual-annotati
import { ProjectWrapper } from '../state/model/project.wrapper'; import { ProjectWrapper } from '../state/model/project.wrapper';
import { AddEditDictionaryDialogComponent } from '../screens/admin/dictionary-listing-screen/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component'; import { AddEditDictionaryDialogComponent } from '../screens/admin/dictionary-listing-screen/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
import { AddEditRuleSetDialogComponent } from '../screens/admin/rule-sets-listing-screen/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component'; import { AddEditRuleSetDialogComponent } from '../screens/admin/rule-sets-listing-screen/add-edit-rule-set-dialog/add-edit-rule-set-dialog.component';
import { DEFAULT_RUL_SET_UUID } from '../utils/rule-set-default';
const dialogConfig = { const dialogConfig = {
width: '662px', width: '662px',
@ -37,6 +37,7 @@ export class DialogService {
private readonly _dialog: MatDialog, private readonly _dialog: MatDialog,
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
private readonly _ruleSetControllerService: RuleSetControllerService,
private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _fileManagementControllerService: FileManagementControllerService, private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _notificationService: NotificationService, private readonly _notificationService: NotificationService,
@ -170,12 +171,12 @@ export class DialogService {
return ref; return ref;
} }
public openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, cb?: Function): MatDialogRef<ConfirmationDialogComponent> { public openDeleteDictionaryDialog($event: MouseEvent, dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation(); $event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig); const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async (result) => { ref.afterClosed().subscribe(async (result) => {
if (result) { if (result) {
await this._dictionaryControllerService.deleteType(dictionary.type, DEFAULT_RUL_SET_UUID).toPromise(); await this._dictionaryControllerService.deleteType(dictionary.type, ruleSetId).toPromise();
if (cb) cb(); if (cb) cb();
} }
}); });
@ -187,6 +188,8 @@ export class DialogService {
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig); const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async (result) => { ref.afterClosed().subscribe(async (result) => {
if (result) { if (result) {
await this._ruleSetControllerService.getAllRuleSets(ruleSet.ruleSetId).toPromise();
await this._appStateService.loadAllRuleSets();
if (cb) cb(); if (cb) cb();
} }
}); });
@ -276,10 +279,10 @@ export class DialogService {
return ref; return ref;
} }
public openAddEditDictionaryDialog(dictionary: TypeValue, cb?: Function): MatDialogRef<AddEditDictionaryDialogComponent> { public openAddEditDictionaryDialog(dictionary: TypeValue, ruleSetId: string, cb?: Function): MatDialogRef<AddEditDictionaryDialogComponent> {
const ref = this._dialog.open(AddEditDictionaryDialogComponent, { const ref = this._dialog.open(AddEditDictionaryDialogComponent, {
...dialogConfig, ...dialogConfig,
data: dictionary, data: { dictionary, ruleSetId },
autoFocus: true autoFocus: true
}); });

View File

@ -1,4 +1,4 @@
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { AppStateService } from '../../state/app-state.service'; import { AppStateService } from '../../state/app-state.service';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@ -10,7 +10,6 @@ import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-red
import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service'; import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service';
import { ManualAnnotationResponse } from '../../screens/file/model/manual-annotation-response'; import { ManualAnnotationResponse } from '../../screens/file/model/manual-annotation-response';
import { PermissionsService } from '../../common/service/permissions.service'; import { PermissionsService } from '../../common/service/permissions.service';
import { DEFAULT_RUL_SET_UUID } from '../../utils/rule-set-default';
export interface LegalBasisOption { export interface LegalBasisOption {
label?: string; label?: string;
@ -57,7 +56,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
) {} ) {}
async ngOnInit() { async ngOnInit() {
this._legalBasisMappingControllerService.getLegalBasisMapping(DEFAULT_RUL_SET_UUID).subscribe((data) => { this._legalBasisMappingControllerService.getLegalBasisMapping(this._appStateService.activeProject.ruleSetId).subscribe((data) => {
for (const key of Object.keys(data.reasonByLegalBasis)) { for (const key of Object.keys(data.reasonByLegalBasis)) {
this.legalOptions.push({ this.legalOptions.push({
legalBasis: key, legalBasis: key,
@ -80,8 +79,8 @@ export class ManualAnnotationDialogComponent implements OnInit {
comment: this.isDocumentAdmin ? [null] : [null, Validators.required] comment: this.isDocumentAdmin ? [null] : [null, Validators.required]
}); });
for (const key of Object.keys(this._appStateService.dictionaryData)) { for (const key of Object.keys(this._appStateService.dictionaryData[this._appStateService.activeProject.ruleSetId])) {
const dictionaryData = this._appStateService.dictionaryData[key]; const dictionaryData = this._appStateService.getDictionaryTypeValue(key);
if (!dictionaryData.virtual && !(dictionaryData.type.indexOf('recommendation_') === 0)) { if (!dictionaryData.virtual && !(dictionaryData.type.indexOf('recommendation_') === 0)) {
if (!dictionaryData.hint && dictionaryData.type !== 'manual') { if (!dictionaryData.hint && dictionaryData.type !== 'manual') {
this.redactionDictionaries.push(dictionaryData); this.redactionDictionaries.push(dictionaryData);

View File

@ -1,4 +1,4 @@
import { Component, Inject, OnInit } from '@angular/core'; import { Component, Inject } from '@angular/core';
import { AppStateService } from '../../../../state/app-state.service'; import { AppStateService } from '../../../../state/app-state.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
@ -6,7 +6,6 @@ import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { NotificationService, NotificationType } from '../../../../notification/notification.service'; import { NotificationService, NotificationType } from '../../../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DEFAULT_RUL_SET_UUID } from '../../../../utils/rule-set-default';
@Component({ @Component({
selector: 'redaction-add-edit-dictionary-dialog', selector: 'redaction-add-edit-dictionary-dialog',
@ -15,6 +14,8 @@ import { DEFAULT_RUL_SET_UUID } from '../../../../utils/rule-set-default';
}) })
export class AddEditDictionaryDialogComponent { export class AddEditDictionaryDialogComponent {
dictionaryForm: FormGroup; dictionaryForm: FormGroup;
public readonly dictionary: TypeValue;
private readonly _ruleSetId: string;
constructor( constructor(
private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _dictionaryControllerService: DictionaryControllerService,
@ -23,8 +24,10 @@ export class AddEditDictionaryDialogComponent {
private readonly _notificationService: NotificationService, private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
public dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>, public dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
@Inject(MAT_DIALOG_DATA) public dictionary: TypeValue @Inject(MAT_DIALOG_DATA) public data: { dictionary: TypeValue; ruleSetId: string }
) { ) {
this.dictionary = data.dictionary;
this._ruleSetId = data.ruleSetId;
this.dictionaryForm = this._formBuilder.group({ this.dictionaryForm = this._formBuilder.group({
type: [this.dictionary?.type, Validators.required], type: [this.dictionary?.type, Validators.required],
rank: [this.dictionary?.rank, Validators.required], rank: [this.dictionary?.rank, Validators.required],
@ -60,14 +63,13 @@ export class AddEditDictionaryDialogComponent {
let observable: Observable<any>; let observable: Observable<any>;
if (this.dictionary) { if (this.dictionary) {
// edit mode // edit mode
observable = this._dictionaryControllerService.updateType(typeValue, typeValue.type, DEFAULT_RUL_SET_UUID); observable = this._dictionaryControllerService.updateType(typeValue, typeValue.type, this._ruleSetId);
} else { } else {
// create mode // create mode
typeValue.ruleSetId = DEFAULT_RUL_SET_UUID; typeValue.ruleSetId = this._ruleSetId;
observable = this._dictionaryControllerService.addType(typeValue); observable = this._dictionaryControllerService.addType(typeValue);
} }
//
observable.subscribe( observable.subscribe(
() => { () => {
this.dialogRef.close({ dictionary: typeValue }); this.dialogRef.close({ dictionary: typeValue });
@ -94,7 +96,8 @@ export class AddEditDictionaryDialogComponent {
hexColor: this.dictionaryForm.get('hexColor').value, hexColor: this.dictionaryForm.get('hexColor').value,
hint: this.dictionaryForm.get('hint').value, hint: this.dictionaryForm.get('hint').value,
type: this.dictionaryForm.get('type').value, type: this.dictionaryForm.get('type').value,
rank: this.dictionaryForm.get('rank').value rank: this.dictionaryForm.get('rank').value,
ruleSetId: this._ruleSetId
}; };
} }
} }

View File

@ -5,7 +5,7 @@
<redaction-rule-set-view-switch [screen]="'dictionaries'"></redaction-rule-set-view-switch> <redaction-rule-set-view-switch [screen]="'dictionaries'"></redaction-rule-set-view-switch>
<div class="flex-1 actions"> <div class="flex-1 actions">
<redaction-rule-set-actions (loadRuleSetsData)="loadRuleSetsData()" [ruleSet]="ruleSet"> </redaction-rule-set-actions> <redaction-rule-set-actions (loadRuleSetsData)="loadRuleSetsData()"> </redaction-rule-set-actions>
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="before" icon="red:close"></redaction-circle-button> <redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="before" icon="red:close"></redaction-circle-button>
</div> </div>

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { DoughnutChartConfig } from '../../../components/simple-doughnut-chart/simple-doughnut-chart.component'; import { DoughnutChartConfig } from '../../../components/simple-doughnut-chart/simple-doughnut-chart.component';
import { DictionaryControllerService, RuleSetModel, TypeValue } from '@redaction/red-ui-http'; import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
import { SortingOption, SortingService } from '../../../utils/sorting.service'; import { SortingOption, SortingService } from '../../../utils/sorting.service';
import { DialogService } from '../../../dialogs/dialog.service'; import { DialogService } from '../../../dialogs/dialog.service';
import { AppStateService } from '../../../state/app-state.service'; import { AppStateService } from '../../../state/app-state.service';
@ -9,7 +9,6 @@ import { forkJoin } from 'rxjs';
import { PermissionsService } from '../../../common/service/permissions.service'; import { PermissionsService } from '../../../common/service/permissions.service';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { debounce } from '../../../utils/debounce'; import { debounce } from '../../../utils/debounce';
import { DEFAULT_RUL_SET_UUID } from '../../../utils/rule-set-default';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
@Component({ @Component({
@ -23,24 +22,23 @@ export class DictionaryListingScreenComponent implements OnInit {
public displayedDictionaries: TypeValue[]; public displayedDictionaries: TypeValue[];
public selectedDictKeys: string[] = []; public selectedDictKeys: string[] = [];
public searchForm: FormGroup; public searchForm: FormGroup;
public ruleSet: RuleSetModel;
constructor( constructor(
private readonly _dialogService: DialogService, private readonly _dialogService: DialogService,
private readonly _sortingService: SortingService, private readonly _sortingService: SortingService,
private readonly _formBuilder: FormBuilder, private readonly _formBuilder: FormBuilder,
private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
private readonly _actr: ActivatedRoute,
public readonly permissionsService: PermissionsService public readonly permissionsService: PermissionsService
) { ) {
this._appStateService.activateRuleSet(_activatedRoute.snapshot.params.ruleSetId);
this.searchForm = this._formBuilder.group({ this.searchForm = this._formBuilder.group({
query: [''] query: ['']
}); });
this.searchForm.valueChanges.subscribe((value) => this._executeSearch(value)); this.searchForm.valueChanges.subscribe((value) => this._executeSearch(value));
this.ruleSet = this._appStateService.getRuleSetById(this._actr.snapshot.params.ruleSetId);
} }
ngOnInit(): void { ngOnInit(): void {
@ -54,15 +52,14 @@ export class DictionaryListingScreenComponent implements OnInit {
} }
private _loadDictionaryData() { private _loadDictionaryData() {
this._appStateService.reset(); const appStateDictionaryData = this._appStateService.dictionaryData[this._appStateService.activeRuleSetId];
const appStateDictionaryData = this._appStateService.dictionaryData;
this.dictionaries = Object.keys(appStateDictionaryData) this.dictionaries = Object.keys(appStateDictionaryData)
.map((key) => appStateDictionaryData[key]) .map((key) => appStateDictionaryData[key])
.filter((d) => !d.virtual || d.type === 'false_positive'); .filter((d) => !d.virtual || d.type === 'false_positive');
this.displayedDictionaries = [...this.dictionaries]; this.displayedDictionaries = [...this.dictionaries];
const dataObs = []; const dataObs = [];
this.dictionaries.forEach((item) => { this.dictionaries.forEach((item) => {
const observable = this._dictionaryControllerService.getDictionaryForType(item.type, DEFAULT_RUL_SET_UUID).pipe( const observable = this._dictionaryControllerService.getDictionaryForType(item.type, this._appStateService.activeRuleSetId).pipe(
tap((values) => { tap((values) => {
item.entries = values.entries ? values.entries : []; item.entries = values.entries ? values.entries : [];
}) })
@ -126,7 +123,7 @@ export class DictionaryListingScreenComponent implements OnInit {
} }
openAddEditDictionaryDialog(dict?: TypeValue) { openAddEditDictionaryDialog(dict?: TypeValue) {
this._dialogService.openAddEditDictionaryDialog(dict, async (newDictionary) => { this._dialogService.openAddEditDictionaryDialog(dict, this._appStateService.activeRuleSetId, async (newDictionary) => {
if (newDictionary) { if (newDictionary) {
await this._appStateService.loadDictionaryData(); await this._appStateService.loadDictionaryData();
this._loadDictionaryData(); this._loadDictionaryData();
@ -140,13 +137,13 @@ export class DictionaryListingScreenComponent implements OnInit {
} }
openDeleteDictionaryDialog($event: any, dict: TypeValue) { openDeleteDictionaryDialog($event: any, dict: TypeValue) {
this._dialogService.openDeleteDictionaryDialog($event, dict, async () => { this._dialogService.openDeleteDictionaryDialog($event, dict, this._appStateService.activeRuleSetId, async () => {
await this._appStateService.loadDictionaryData(); await this._appStateService.loadDictionaryData();
this._loadDictionaryData(); this._loadDictionaryData();
}); });
} }
public loadRuleSetsData(): void { public async loadRuleSetsData(): Promise<void> {
console.log('load rule sets data'); await this._appStateService.loadAllRuleSets();
} }
} }

View File

@ -132,7 +132,10 @@
</div> </div>
<div class="indicator"> <div class="indicator">
<redaction-dictionary-annotation-icon [dictionaryKey]="dictionary.hint ? 'hint' : 'redaction'"></redaction-dictionary-annotation-icon> <redaction-dictionary-annotation-icon
[dictionaryKey]="dictionary.hint ? 'hint' : 'redaction'"
[ruleSetId]="dictionary.ruleSetId"
></redaction-dictionary-annotation-icon>
<div class="large-label"> <div class="large-label">
{{ (dictionary.hint ? 'hint' : 'redaction') | translate }} {{ (dictionary.hint ? 'hint' : 'redaction') | translate }}
</div> </div>

View File

@ -5,10 +5,6 @@
height: calc(100% - 50px); height: calc(100% - 50px);
} }
.changes-box {
right: 403px;
}
.left-container { .left-container {
width: calc(100vw - 353px); width: calc(100vw - 353px);
padding: 15px; padding: 15px;

View File

@ -10,7 +10,6 @@ import { NotificationService, NotificationType } from '../../../notification/not
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { DEFAULT_RUL_SET_UUID } from '../../../utils/rule-set-default';
import { ComponentHasChanges } from '../../../utils/can-deactivate.guard'; import { ComponentHasChanges } from '../../../utils/can-deactivate.guard';
declare var ace; declare var ace;
@ -59,18 +58,13 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges {
private readonly _appStateService: AppStateService private readonly _appStateService: AppStateService
) { ) {
super(_translateService); super(_translateService);
this._activatedRoute.params.subscribe((params) => { this._appStateService.activateDictionary(this._activatedRoute.snapshot.params.type, this._activatedRoute.snapshot.params.ruleSetId);
this.dictionary = this._appStateService.dictionaryData[params.type]; this.dictionary = this._appStateService.activeDictionary;
if (!this.dictionary) { this._initialize();
this._router.navigate(['/ui/admin/dictionaries']);
} else {
this._initialize();
}
});
} }
private _initialize() { private _initialize() {
this._dictionaryControllerService.getDictionaryForType(this.dictionary.type, DEFAULT_RUL_SET_UUID).subscribe( this._dictionaryControllerService.getDictionaryForType(this.dictionary.type, this.dictionary.ruleSetId).subscribe(
(data) => { (data) => {
this.initialDictionaryEntries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' })); this.initialDictionaryEntries = data.entries.sort((str1, str2) => str1.localeCompare(str2, undefined, { sensitivity: 'accent' }));
this.revert(); this.revert();
@ -83,7 +77,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges {
openEditDictionaryDialog($event: any) { openEditDictionaryDialog($event: any) {
$event.stopPropagation(); $event.stopPropagation();
this._dialogService.openAddEditDictionaryDialog(this.dictionary, async (newDictionary) => { this._dialogService.openAddEditDictionaryDialog(this.dictionary, this.dictionary.ruleSetId, async (newDictionary) => {
if (newDictionary) { if (newDictionary) {
await this._appStateService.loadDictionaryData(); await this._appStateService.loadDictionaryData();
this.dictionary = this._appStateService.dictionaryData[this.dictionary.type]; this.dictionary = this._appStateService.dictionaryData[this.dictionary.type];
@ -92,7 +86,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges {
} }
openDeleteDictionaryDialog($event: any) { openDeleteDictionaryDialog($event: any) {
this._dialogService.openDeleteDictionaryDialog($event, this.dictionary, async () => { this._dialogService.openDeleteDictionaryDialog($event, this.dictionary, this.dictionary.ruleSetId, async () => {
await this._appStateService.loadDictionaryData(); await this._appStateService.loadDictionaryData();
this._router.navigate(['/ui/admin/dictionaries']); this._router.navigate(['/ui/admin/dictionaries']);
}); });
@ -186,9 +180,9 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges {
this.processing = true; this.processing = true;
let obs: Observable<any>; let obs: Observable<any>;
if (entriesToAdd.length > 0) { if (entriesToAdd.length > 0) {
obs = this._dictionaryControllerService.addEntry(entriesToAdd, this.dictionary.type, DEFAULT_RUL_SET_UUID, true); obs = this._dictionaryControllerService.addEntry(entriesToAdd, this.dictionary.type, this.dictionary.ruleSetId, true);
} else { } else {
obs = this._dictionaryControllerService.deleteEntries(this.initialDictionaryEntries, this.dictionary.type, DEFAULT_RUL_SET_UUID); obs = this._dictionaryControllerService.deleteEntries(this.initialDictionaryEntries, this.dictionary.type, this.dictionary.ruleSetId);
} }
obs.subscribe( obs.subscribe(

View File

@ -54,7 +54,8 @@ export class AddEditRuleSetDialogComponent {
ruleSetId: this.ruleSet?.ruleSetId, ruleSetId: this.ruleSet?.ruleSetId,
...this.ruleSetForm.getRawValue() ...this.ruleSetForm.getRawValue()
}; };
const res = await this._ruleSetController.createOrUpdateRuleSet(ruleSet).toPromise(); await this._ruleSetController.createOrUpdateRuleSet(ruleSet).toPromise();
await this._appStateService.loadAllRuleSets();
this.dialogRef.close({ ruleSet }); this.dialogRef.close({ ruleSet });
} }
} }

View File

@ -5,7 +5,7 @@
<redaction-rule-set-view-switch [screen]="'rules'"></redaction-rule-set-view-switch> <redaction-rule-set-view-switch [screen]="'rules'"></redaction-rule-set-view-switch>
<div class="flex-1 actions"> <div class="flex-1 actions">
<redaction-rule-set-actions (loadRuleSetsData)="loadRuleSetsData()" [ruleSet]="ruleSet"> </redaction-rule-set-actions> <redaction-rule-set-actions (loadRuleSetsData)="loadRuleSetsData()"> </redaction-rule-set-actions>
<redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="before" icon="red:close"></redaction-circle-button> <redaction-circle-button [routerLink]="['../..']" tooltip="common.close" tooltipPosition="before" icon="red:close"></redaction-circle-button>
</div> </div>

View File

@ -6,10 +6,6 @@
@include inset-shadow; @include inset-shadow;
} }
.changes-box {
right: 40px;
}
.page-header .actions { .page-header .actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View File

@ -5,7 +5,6 @@ import { RulesControllerService, RuleSetModel } from '@redaction/red-ui-http';
import { NotificationService, NotificationType } from '../../../notification/notification.service'; import { NotificationService, NotificationType } from '../../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { DEFAULT_RUL_SET_UUID } from '../../../utils/rule-set-default';
import { ComponentHasChanges } from '../../../utils/can-deactivate.guard'; import { ComponentHasChanges } from '../../../utils/can-deactivate.guard';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { AppStateService } from '../../../state/app-state.service'; import { AppStateService } from '../../../state/app-state.service';
@ -26,7 +25,6 @@ export class RulesScreenComponent extends ComponentHasChanges {
public currentLines: string[] = []; public currentLines: string[] = [];
public changedLines: number[] = []; public changedLines: number[] = [];
public activeEditMarkers: any[] = []; public activeEditMarkers: any[] = [];
public ruleSet: RuleSetModel;
@ViewChild('editorComponent', { static: true }) @ViewChild('editorComponent', { static: true })
editorComponent: AceEditorComponent; editorComponent: AceEditorComponent;
@ -40,15 +38,15 @@ export class RulesScreenComponent extends ComponentHasChanges {
private readonly _appStateService: AppStateService, private readonly _appStateService: AppStateService,
private readonly _notificationService: NotificationService, private readonly _notificationService: NotificationService,
protected readonly _translateService: TranslateService, protected readonly _translateService: TranslateService,
private readonly _actr: ActivatedRoute private readonly _activatedRoute: ActivatedRoute
) { ) {
super(_translateService); super(_translateService);
this._appStateService.activateRuleSet(_activatedRoute.snapshot.params.ruleSetId);
this._initialize(); this._initialize();
this.ruleSet = this._appStateService.getRuleSetById(this._actr.snapshot.params.ruleSetId);
} }
private _initialize() { private _initialize() {
this._rulesControllerService.downloadRules(DEFAULT_RUL_SET_UUID).subscribe( this._rulesControllerService.downloadRules(this._appStateService.activeRuleSetId).subscribe(
(rules) => { (rules) => {
this.rules = rules.rules; this.rules = rules.rules;
this.revert(); this.revert();
@ -90,16 +88,22 @@ export class RulesScreenComponent extends ComponentHasChanges {
public async save(): Promise<void> { public async save(): Promise<void> {
this.processing = true; this.processing = true;
this._rulesControllerService.uploadRules({ rules: this.editorComponent.getEditor().getValue(), ruleSetId: DEFAULT_RUL_SET_UUID }).subscribe( this._rulesControllerService
() => { .uploadRules({ rules: this.editorComponent.getEditor().getValue(), ruleSetId: this._appStateService.activeRuleSetId })
this._initialize(); .subscribe(
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.success.generic'), null, NotificationType.SUCCESS); () => {
}, this._initialize();
() => { this._notificationService.showToastNotification(
this.processing = false; this._translateService.instant('rules-screen.success.generic'),
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.error.generic'), null, NotificationType.ERROR); null,
} NotificationType.SUCCESS
); );
},
() => {
this.processing = false;
this._notificationService.showToastNotification(this._translateService.instant('rules-screen.error.generic'), null, NotificationType.ERROR);
}
);
} }
public revert(): void { public revert(): void {

View File

@ -1,8 +1,10 @@
<section> <section>
<div class="page-header"> <div class="page-header">
<redaction-admin-breadcrumbs class="flex-1" [root]="true"></redaction-admin-breadcrumbs> <redaction-admin-breadcrumbs class="flex-1"></redaction-admin-breadcrumbs>
<div class="actions"> <redaction-rule-set-view-switch [screen]="'watermark'"></redaction-rule-set-view-switch>
<div class="actions flex-1">
<redaction-circle-button <redaction-circle-button
class="ml-6" class="ml-6"
*ngIf="permissionsService.isUser()" *ngIf="permissionsService.isUser()"

View File

@ -10,10 +10,6 @@
.viewer { .viewer {
height: 100%; height: 100%;
} }
.changes-box {
right: 40px;
}
} }
.right-container { .right-container {

View File

@ -10,7 +10,7 @@ import { debounce } from '../../../utils/debounce';
import { WatermarkControllerService, WatermarkModel } from '@redaction/red-ui-http'; import { WatermarkControllerService, WatermarkModel } from '@redaction/red-ui-http';
import { NotificationService, NotificationType } from '../../../notification/notification.service'; import { NotificationService, NotificationType } from '../../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { DEFAULT_RUL_SET_UUID } from '../../../utils/rule-set-default'; import { ActivatedRoute } from '@angular/router';
export const DEFAULT_WATERMARK: WatermarkModel = { export const DEFAULT_WATERMARK: WatermarkModel = {
text: text:
@ -32,7 +32,6 @@ export const DEFAULT_WATERMARK: WatermarkModel = {
}) })
export class WatermarkScreenComponent implements OnInit { export class WatermarkScreenComponent implements OnInit {
private _instance: WebViewerInstance; private _instance: WebViewerInstance;
private _watermark: WatermarkModel = {}; private _watermark: WatermarkModel = {};
@ViewChild('viewer', { static: true }) @ViewChild('viewer', { static: true })
@ -62,8 +61,10 @@ export class WatermarkScreenComponent implements OnInit {
private readonly _fileDownloadService: FileDownloadService, private readonly _fileDownloadService: FileDownloadService,
private readonly _http: HttpClient, private readonly _http: HttpClient,
private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _formBuilder: FormBuilder private readonly _formBuilder: FormBuilder,
private readonly _activatedRoute: ActivatedRoute
) { ) {
this.appStateService.activateRuleSet(_activatedRoute.snapshot.params.ruleSetId);
this._initForm(); this._initForm();
} }
@ -72,7 +73,7 @@ export class WatermarkScreenComponent implements OnInit {
} }
private _loadWatermark() { private _loadWatermark() {
this._watermarkControllerService.getWatermark(DEFAULT_RUL_SET_UUID).subscribe( this._watermarkControllerService.getWatermark(this.appStateService.activeRuleSetId).subscribe(
(watermark) => { (watermark) => {
this._watermark = watermark; this._watermark = watermark;
this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' }); this.configForm.setValue({ ...this._watermark, fontType: 'sans-serif', orientation: 'diagonal' });
@ -95,7 +96,7 @@ export class WatermarkScreenComponent implements OnInit {
const watermark = { const watermark = {
...this.configForm.getRawValue() ...this.configForm.getRawValue()
}; };
this._watermarkControllerService.saveWatermark(watermark, DEFAULT_RUL_SET_UUID).subscribe( this._watermarkControllerService.saveWatermark(watermark, this.appStateService.activeRuleSetId).subscribe(
() => { () => {
this._loadWatermark(); this._loadWatermark();
this._notificationService.showToastNotification( this._notificationService.showToastNotification(

View File

@ -150,6 +150,10 @@
} }
} }
redaction-dictionary-annotation-icon {
margin-right: 8px;
}
.mr-16 { .mr-16 {
margin-right: 16px; margin-right: 16px;
} }

View File

@ -182,7 +182,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this.activeViewer.annotManager.deleteAnnotations(existingAnnotations, true, true); this.activeViewer.annotManager.deleteAnnotations(existingAnnotations, true, true);
} }
this.annotations = this.fileData.getAnnotations( this.annotations = this.fileData.getAnnotations(
this.appStateService.dictionaryData, this.appStateService.dictionaryData[this.appStateService.activeProject.ruleSetId],
this.permissionsService.currentUser, this.permissionsService.currentUser,
this.userPreferenceService.areDevFeaturesEnabled this.userPreferenceService.areDevFeaturesEnabled
); );

View File

@ -6,7 +6,6 @@ import {
Project, Project,
ProjectControllerService, ProjectControllerService,
ReanalysisControllerService, ReanalysisControllerService,
RulesControllerService,
RuleSetControllerService, RuleSetControllerService,
RuleSetModel, RuleSetModel,
StatusControllerService, StatusControllerService,
@ -17,33 +16,32 @@ import { NotificationService, NotificationType } from '../notification/notificat
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
import { forkJoin } from 'rxjs'; import { forkJoin, Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { humanize } from '../utils/functions'; import { humanize } from '../utils/functions';
import { FileStatusWrapper } from '../screens/file/model/file-status.wrapper'; import { FileStatusWrapper } from '../screens/file/model/file-status.wrapper';
import { ProjectWrapper } from './model/project.wrapper'; import { ProjectWrapper } from './model/project.wrapper';
import { saveAs } from 'file-saver'; import { saveAs } from 'file-saver';
import { DEFAULT_RUL_SET_UUID } from '../utils/rule-set-default';
export interface AppState { export interface AppState {
projects: ProjectWrapper[]; projects: ProjectWrapper[];
ruleSets: RuleSetModel[]; ruleSets: RuleSetModel[];
activeProjectId: string; activeProjectId: string;
activeFileId: string; activeFileId: string;
activeRuleSetId: string;
activeDictionaryType: string;
totalAnalysedPages?: number; totalAnalysedPages?: number;
totalDocuments?: number; totalDocuments?: number;
totalPeople?: number; totalPeople?: number;
dictionaryVersion?: number; versions: { [key: string]: { dictionaryVersion?: number; rulesVersion?: number } };
ruleVersion?: number;
} }
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
// TODO everything needs to be stored ruleSetBased in either an Object ( Dict ) or Map Structure.
export class AppStateService { export class AppStateService {
private _appState: AppState; private _appState: AppState;
private _dictionaryData: { [key: string]: TypeValue } = null; private _dictionaryData: { [key: string]: { [key: string]: TypeValue } } = null;
public fileChanged = new EventEmitter<FileStatusWrapper>(); public fileChanged = new EventEmitter<FileStatusWrapper>();
public fileReanalysed = new EventEmitter<FileStatusWrapper>(); public fileReanalysed = new EventEmitter<FileStatusWrapper>();
@ -64,7 +62,10 @@ export class AppStateService {
projects: [], projects: [],
ruleSets: [], ruleSets: [],
activeProjectId: null, activeProjectId: null,
activeFileId: null activeFileId: null,
activeRuleSetId: null,
activeDictionaryType: null,
versions: {}
}; };
} }
@ -79,28 +80,41 @@ export class AppStateService {
if (!fileStatus) { if (!fileStatus) {
fileStatus = this.activeFile; fileStatus = this.activeFile;
} }
return fileStatus.dictionaryVersion === this.dictionaryVersion && fileStatus.rulesVersion === this.rulesVersion; return fileStatus.dictionaryVersion === this.dictionaryVersion() && fileStatus.rulesVersion === this.rulesVersion();
} }
get dictionaryVersion() { public dictionaryVersion(ruleSetId?: string) {
return this._appState.dictionaryVersion; if (!ruleSetId) {
ruleSetId = this.activeProject.ruleSetId;
}
return this._appState.versions[ruleSetId].dictionaryVersion;
} }
get rulesVersion() { public rulesVersion(ruleSetId?: string) {
return this._appState.ruleVersion; if (!ruleSetId) {
ruleSetId = this.activeProject.ruleSetId;
}
return this._appState.versions[ruleSetId].rulesVersion;
} }
get dictionaryData(): { [key: string]: TypeValue } { get dictionaryData(): { [key: string]: { [key: string]: TypeValue } } {
return this._dictionaryData; return this._dictionaryData;
} }
getDictionaryColor(type?: string) { getDictionaryColor(type?: string, ruleSetId?: string) {
const color = this._dictionaryData[type]?.hexColor; if (!ruleSetId && this.activeProject) {
return color ? color : this._dictionaryData['default'].hexColor; ruleSetId = this.activeProject.ruleSetId;
}
const color = this._dictionaryData[ruleSetId][type]?.hexColor;
return color ? color : this._dictionaryData[ruleSetId]['default'].hexColor;
} }
getDictionaryLabel(type: string) { getDictionaryLabel(type: string, ruleSetId?: string) {
return this._dictionaryData[type].label; if (!ruleSetId && this.activeProject) {
ruleSetId = this.activeProject.ruleSetId;
}
return this._dictionaryData[ruleSetId][type].label;
} }
get aggregatedFiles(): FileStatusWrapper[] { get aggregatedFiles(): FileStatusWrapper[] {
@ -111,12 +125,45 @@ export class AppStateService {
return result; return result;
} }
get activeRuleSetId(): string {
return this._appState.activeRuleSetId;
}
get activeRuleSet(): RuleSetModel {
return this.getRuleSetById(this.activeRuleSetId);
}
public get ruleSets(): RuleSetModel[] {
return this._appState.ruleSets;
}
public getRuleSetById(id: string): RuleSetModel {
return this.ruleSets.find((rs) => rs.ruleSetId === id);
}
get activeDictionaryType(): string {
return this._appState.activeDictionaryType;
}
get activeDictionary(): TypeValue {
return this.dictionaryData[this.activeRuleSetId][this.activeDictionaryType];
}
getDictionaryTypeValue(key: string, ruleSetId?: string) {
if (!ruleSetId && this.activeProject) {
ruleSetId = this.activeProject.ruleSetId;
}
const data = this._dictionaryData[ruleSetId][key];
return data ? data : this._dictionaryData[ruleSetId]['default'];
}
get activeProjectId(): string { get activeProjectId(): string {
return this._appState.activeProjectId; return this._appState.activeProjectId;
} }
get activeProject(): ProjectWrapper { get activeProject(): ProjectWrapper {
return this._appState.projects.find((p) => p.projectId === this._appState.activeProjectId); return this._appState.projects.find((p) => p.projectId === this.activeProjectId);
} }
get allProjects(): ProjectWrapper[] { get allProjects(): ProjectWrapper[] {
@ -155,14 +202,6 @@ export class AppStateService {
return this.getProjectById(projectId).files.find((file) => file.fileId === fileId); return this.getProjectById(projectId).files.find((file) => file.fileId === fileId);
} }
public get ruleSets(): RuleSetModel[] {
return this._appState.ruleSets;
}
public getRuleSetById(id: string): RuleSetModel {
return this.ruleSets.find((pt) => pt.ruleSetId === id);
}
async loadAllProjects() { async loadAllProjects() {
const projects = await this._projectControllerService.getProjects().toPromise(); const projects = await this._projectControllerService.getProjects().toPromise();
if (projects) { if (projects) {
@ -274,27 +313,45 @@ export class AppStateService {
if (!this.activeProject) { if (!this.activeProject) {
this._appState.activeProjectId = null; this._appState.activeProjectId = null;
this._router.navigate(['/ui/projects']); this._router.navigate(['/ui/projects']);
return;
} }
return this.activeProject;
} }
activateFile(projectId: string, fileId: string) { activateFile(projectId: string, fileId: string) {
const activeProject = this.activateProject(projectId); this.activateProject(projectId);
if (activeProject) { if (this.activeProject) {
this._appState.activeFileId = fileId; this._appState.activeFileId = fileId;
if (!this.activeFile) { if (!this.activeFile) {
this._appState.activeFileId = null; this._appState.activeFileId = null;
this._router.navigate(['/ui/projects/' + projectId]); this._router.navigate(['/ui/projects/' + projectId]);
return;
} }
return this.activateFile; }
}
activateRuleSet(ruleSetId: string) {
this._appState.activeRuleSetId = ruleSetId;
this._appState.activeDictionaryType = null;
if (!this.activeRuleSet) {
this._appState.activeRuleSetId = null;
this._router.navigate(['/ui/admin/project-templates']);
}
}
activateDictionary(dictionaryType: string, ruleSetId: string) {
this.activateRuleSet(ruleSetId);
if (this.activeRuleSet) {
this._appState.activeDictionaryType = dictionaryType;
if (!this.activeDictionary) {
this._appState.activeDictionaryType = null;
this._router.navigate(['/ui/admin/project-templates/' + this.activeRuleSetId]);
}
} }
} }
reset() { reset() {
this._appState.activeFileId = null; this._appState.activeFileId = null;
this._appState.activeProjectId = null; this._appState.activeProjectId = null;
this._appState.activeRuleSetId = null;
this._appState.activeDictionaryType = null;
} }
deleteProject(project: ProjectWrapper) { deleteProject(project: ProjectWrapper) {
@ -319,8 +376,6 @@ export class AppStateService {
async addOrUpdateProject(project: Project) { async addOrUpdateProject(project: Project) {
try { try {
// TODO fix after adding this to the Dialog
project.ruleSetId = DEFAULT_RUL_SET_UUID;
const updatedProject = await this._projectControllerService.createOrUpdateProject(project).toPromise(); const updatedProject = await this._projectControllerService.createOrUpdateProject(project).toPromise();
let foundProject = this._appState.projects.find((p) => p.project.projectId === updatedProject.projectId); let foundProject = this._appState.projects.find((p) => p.project.projectId === updatedProject.projectId);
if (foundProject) { if (foundProject) {
@ -407,120 +462,128 @@ export class AppStateService {
} }
} }
async loadDictionaryData() { getDictionaryDataForRuleSetObservables(ruleSetId: string, dictionaryData: {}): Observable<any>[] {
this._dictionaryData = {}; const typeObs = this._dictionaryControllerService.getAllTypes(ruleSetId).pipe(
const typeObs = this._dictionaryControllerService.getAllTypes(DEFAULT_RUL_SET_UUID).pipe(
tap((typesResponse) => { tap((typesResponse) => {
for (const type of typesResponse.types) { for (const type of typesResponse.types) {
this._dictionaryData[type.type] = type; dictionaryData[type.type] = type;
this._dictionaryData[type.type].virtual = type.type === 'false_positive'; dictionaryData[type.type].virtual = type.type === 'false_positive';
dictionaryData[type.type].label = humanize(type.type, false);
} }
}) })
); );
const colorsObs = this._dictionaryControllerService.getColors(DEFAULT_RUL_SET_UUID).pipe(
const colorsObs = this._dictionaryControllerService.getColors(ruleSetId).pipe(
tap((colors) => { tap((colors) => {
// declined // declined
this._dictionaryData['declined-suggestion'] = { dictionaryData['declined-suggestion'] = {
hexColor: colors.notRedacted, hexColor: colors.notRedacted,
type: 'declined-suggestion', type: 'declined-suggestion',
virtual: true virtual: true
}; };
// manual // manual
this._dictionaryData['manual'] = { dictionaryData['manual'] = {
hexColor: colors.defaultColor, hexColor: colors.defaultColor,
type: 'manual', type: 'manual',
virtual: true virtual: true
}; };
// dictionary actions // dictionary actions
this._dictionaryData['add-dictionary'] = { dictionaryData['add-dictionary'] = {
hexColor: '#dd4d50', hexColor: '#dd4d50',
type: 'add-dictionary', type: 'add-dictionary',
virtual: true virtual: true
}; };
this._dictionaryData['remove-dictionary'] = { dictionaryData['remove-dictionary'] = {
hexColor: '#dd4d50', hexColor: '#dd4d50',
type: 'remove-dictionary', type: 'remove-dictionary',
virtual: true virtual: true
}; };
// generic suggestions // generic suggestions
this._dictionaryData['suggestion'] = { dictionaryData['suggestion'] = {
hexColor: colors.requestAdd, hexColor: colors.requestAdd,
type: 'suggestion', type: 'suggestion',
virtual: true virtual: true
}; };
// add suggestions // add suggestions
this._dictionaryData['suggestion-add'] = { dictionaryData['suggestion-add'] = {
hexColor: colors.requestAdd, hexColor: colors.requestAdd,
type: 'suggestion-add', type: 'suggestion-add',
virtual: true virtual: true
}; };
this._dictionaryData['suggestion-add-dictionary'] = { dictionaryData['suggestion-add-dictionary'] = {
hexColor: '#5B97DB', hexColor: '#5B97DB',
type: 'suggestion-add', type: 'suggestion-add',
virtual: true virtual: true
}; };
// suggestion remove // suggestion remove
this._dictionaryData['suggestion-remove'] = { dictionaryData['suggestion-remove'] = {
hexColor: colors.requestRemove, hexColor: colors.requestRemove,
type: 'suggestion-add', type: 'suggestion-add',
virtual: true virtual: true
}; };
this._dictionaryData['suggestion-remove-dictionary'] = { dictionaryData['suggestion-remove-dictionary'] = {
hexColor: '#5B97DB', hexColor: '#5B97DB',
type: 'suggestion-add', type: 'suggestion-add',
virtual: true virtual: true
}; };
this._dictionaryData['manual'] = { dictionaryData['manual'] = {
hexColor: colors.defaultColor, hexColor: colors.defaultColor,
type: 'manual', type: 'manual',
virtual: true virtual: true
}; };
this._dictionaryData['ignore'] = { dictionaryData['ignore'] = {
hexColor: colors.notRedacted, hexColor: colors.notRedacted,
type: 'ignore', type: 'ignore',
virtual: true virtual: true
}; };
this._dictionaryData['default'] = { dictionaryData['default'] = {
hexColor: colors.defaultColor, hexColor: colors.defaultColor,
type: 'default', type: 'default',
virtual: true virtual: true
}; };
this._dictionaryData['add'] = { dictionaryData['add'] = {
hexColor: colors.requestAdd, hexColor: colors.requestAdd,
type: 'add', type: 'add',
virtual: true virtual: true
}; };
this._dictionaryData['analysis'] = { dictionaryData['analysis'] = {
hexColor: '#dd4d50', hexColor: '#dd4d50',
type: 'analysis', type: 'analysis',
virtual: true virtual: true
}; };
dictionaryData['hint'] = {
hexColor: '#9398a0',
type: 'hint',
virtual: true,
hint: true
};
dictionaryData['redaction'] = {
hexColor: '#283241',
type: 'redaction',
virtual: true
};
}) })
); );
await forkJoin([typeObs, colorsObs]).toPromise(); return [typeObs, colorsObs];
this._dictionaryData['hint'] = { hexColor: '#9398a0', type: 'hint', virtual: true, hint: true };
this._dictionaryData['redaction'] = {
hexColor: '#283241',
type: 'redaction',
virtual: true
};
for (const key of Object.keys(this._dictionaryData)) {
this._dictionaryData[key].label = humanize(key, false);
}
} }
getDictionaryTypeValue(key: string) { async loadDictionaryData() {
const data = this._dictionaryData[key]; this._dictionaryData = {};
return data ? data : this._dictionaryData['default']; const observables = [];
for (const ruleSet of this.ruleSets) {
this.dictionaryData[ruleSet.ruleSetId] = {};
observables.push(...this.getDictionaryDataForRuleSetObservables(ruleSet.ruleSetId, this.dictionaryData[ruleSet.ruleSetId]));
}
await forkJoin(observables).toPromise();
} }
async updateDictionaryVersion() { async updateDictionaryVersion() {
const result = await this._versionsControllerService.getVersions([DEFAULT_RUL_SET_UUID]).toPromise(); const ruleSetIds = this.ruleSets.map((rs) => rs.ruleSetId);
this._appState.dictionaryVersion = result[DEFAULT_RUL_SET_UUID].dictionaryVersion; this._appState.versions = await this._versionsControllerService.getVersions(ruleSetIds).toPromise();
this._appState.ruleVersion = result[DEFAULT_RUL_SET_UUID].rulesVersion;
} }
} }

View File

@ -82,6 +82,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
bottom: 40px; bottom: 40px;
right: 40px;
border-radius: 8px; border-radius: 8px;
padding: 16px 32px 16px 16px; padding: 16px 32px 16px 16px;
background-color: $white; background-color: $white;

View File

@ -2,8 +2,9 @@
.mat-button-toggle-standalone, .mat-button-toggle-standalone,
.mat-button-toggle-group { .mat-button-toggle-group {
border-radius: 100px !important;
box-shadow: none; box-shadow: none;
border-radius: 100px !important;
width: fit-content;
.mat-button-toggle:not(.mat-button-toggle-checked) { .mat-button-toggle:not(.mat-button-toggle-checked) {
.mat-button-toggle-button { .mat-button-toggle-button {
@ -11,4 +12,9 @@
color: $grey-7; color: $grey-7;
} }
} }
.mat-button-toggle:not(:first-of-type):not(:last-of-type) {
border-left: 1px solid $white;
border-right: 1px solid $white;
}
} }