Merge branch 'master' into VM/RED-2614

This commit is contained in:
Valentin 2021-12-15 10:30:30 +02:00
commit 4c076ffdaa
65 changed files with 303 additions and 259 deletions

View File

@ -1,4 +1,7 @@
{ {
"cli": {
"analytics": "4b8eed12-a1e6-4b7a-9ea2-925b27941271"
},
"version": 1, "version": 1,
"projects": { "projects": {
"common-ui": { "common-ui": {

View File

@ -35,7 +35,6 @@ const routes: Routes = [
component: BaseScreenComponent, component: BaseScreenComponent,
loadChildren: () => import('./modules/dossier/dossiers.module').then(m => m.DossiersModule), loadChildren: () => import('./modules/dossier/dossiers.module').then(m => m.DossiersModule),
canActivate: [CompositeRouteGuard], canActivate: [CompositeRouteGuard],
canDeactivate: [DossiersGuard],
data: { data: {
routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard, DossiersGuard], routeGuards: [AuthGuard, RedRoleGuard, AppStateGuard, DossiersGuard],
requiredRoles: ['RED_USER', 'RED_MANAGER'], requiredRoles: ['RED_USER', 'RED_MANAGER'],

View File

@ -1,4 +1,5 @@
import { Component, ViewContainerRef } from '@angular/core'; import { Component, ViewContainerRef } from '@angular/core';
import { RouterHistoryService } from '@services/router-history.service';
@Component({ @Component({
selector: 'redaction-root', selector: 'redaction-root',
@ -6,6 +7,7 @@ import { Component, ViewContainerRef } from '@angular/core';
styleUrls: ['./app.component.scss'], styleUrls: ['./app.component.scss'],
}) })
export class AppComponent { export class AppComponent {
// View container ref needs to be injected for the color picker to work // ViewContainerRef needs to be injected for the color picker to work
constructor(public viewContainerRef: ViewContainerRef) {} // RouterHistoryService needs to be injected for last dossiers screen to be updated on first app load
constructor(public viewContainerRef: ViewContainerRef, private readonly _routerHistoryService: RouterHistoryService) {}
} }

View File

@ -7,7 +7,6 @@ import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common
import { BaseScreenComponent } from '@components/base-screen/base-screen.component'; import { BaseScreenComponent } from '@components/base-screen/base-screen.component';
import { ApiPathInterceptor } from '@utils/api-path-interceptor'; import { ApiPathInterceptor } from '@utils/api-path-interceptor';
import { MissingTranslationHandler, TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { MissingTranslationHandler, TranslateCompiler, TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { languageInitializer } from '@i18n/language.initializer';
import { LanguageService } from '@i18n/language.service'; import { LanguageService } from '@i18n/language.service';
import { ToastrModule } from 'ngx-toastr'; import { ToastrModule } from 'ngx-toastr';
import { ServiceWorkerModule } from '@angular/service-worker'; import { ServiceWorkerModule } from '@angular/service-worker';

View File

@ -7,7 +7,7 @@
<redaction-breadcrumbs iqserHelpMode="navigate-in-breadcrumbs"></redaction-breadcrumbs> <redaction-breadcrumbs iqserHelpMode="navigate-in-breadcrumbs"></redaction-breadcrumbs>
</div> </div>
<div class="center"> <div class="logo">
<iqser-hidden-action (action)="userPreferenceService.toggleDevFeatures()"> <iqser-hidden-action (action)="userPreferenceService.toggleDevFeatures()">
<iqser-logo icon="red:logo"></iqser-logo> <iqser-logo icon="red:logo"></iqser-logo>
</iqser-hidden-action> </iqser-hidden-action>

View File

@ -1,9 +1,5 @@
<ng-container *ngIf="breadcrumbsService.breadcrumbs$ | async as breadcrumbs"> <ng-container *ngIf="breadcrumbsService.breadcrumbs$ | async as breadcrumbs">
<a <a *ngIf="breadcrumbs.length === 0; else items" class="breadcrumb back" redactionNavigateLastDossiersScreen>
*ngIf="breadcrumbs.length === 0 || (breadcrumbsService.showGoBack$ | async) === true; else items"
class="breadcrumb back"
redactionNavigateLastDossiersScreen
>
<mat-icon svgIcon="iqser:expand"></mat-icon> <mat-icon svgIcon="iqser:expand"></mat-icon>
{{ 'top-bar.navigation-items.back' | translate }} {{ 'top-bar.navigation-items.back' | translate }}
</a> </a>

View File

@ -8,6 +8,7 @@
[itemSize]="80" [itemSize]="80"
[noDataText]="'downloads-list.no-data.title' | translate" [noDataText]="'downloads-list.no-data.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="iqser:download" noDataIcon="iqser:download"
></iqser-table> ></iqser-table>
</div> </div>

View File

@ -1,16 +1,13 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { pluck } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesMapService } from '@services/entity-services/files-map.service';
import { FilesService } from '@services/entity-services/files.service'; import { FilesService } from '@services/entity-services/files.service';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class DossierFilesGuard implements CanActivate, CanDeactivate<unknown> { export class DossierFilesGuard implements CanActivate {
constructor( constructor(
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _breadcrumbsService: BreadcrumbsService,
private readonly _filesMapService: FilesMapService, private readonly _filesMapService: FilesMapService,
private readonly _filesService: FilesService, private readonly _filesService: FilesService,
private readonly _router: Router, private readonly _router: Router,
@ -27,28 +24,6 @@ export class DossierFilesGuard implements CanActivate, CanDeactivate<unknown> {
if (!this._filesMapService.has(dossierId)) { if (!this._filesMapService.has(dossierId)) {
await this._filesService.loadAll(dossierId).toPromise(); await this._filesService.loadAll(dossierId).toPromise();
} }
this._breadcrumbsService.append({
name$: this._dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
routerLink: ['/main', 'dossiers', dossierId],
routerLinkActiveOptions: { exact: true },
});
this._breadcrumbsService.hideGoBack();
return true;
}
canDeactivate(
component: unknown,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot,
) {
const dossierId = currentRoute.paramMap.get('dossierId');
this._breadcrumbsService.remove(['/main', 'dossiers', dossierId]);
if (!nextState.url.startsWith('/main/dossiers')) {
this._breadcrumbsService.showGoBack();
}
return true; return true;
} }
} }

View File

@ -1,39 +1,18 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router'; import { CanActivate, Router } from '@angular/router';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { of } from 'rxjs';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class DossiersGuard implements CanActivate, CanDeactivate<unknown> { export class DossiersGuard implements CanActivate {
constructor( constructor(
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _breadcrumbsService: BreadcrumbsService,
private readonly _translateService: TranslateService, private readonly _translateService: TranslateService,
private readonly _router: Router, private readonly _router: Router,
) {} ) {}
async canActivate(): Promise<boolean> { async canActivate(): Promise<boolean> {
await this._dossiersService.loadAll().toPromise(); await this._dossiersService.loadAll().toPromise();
this._breadcrumbsService.hideGoBack();
this._breadcrumbsService.append({
name$: of(this._translateService.instant('top-bar.navigation-items.dossiers')),
routerLink: ['/main', 'dossiers'],
routerLinkActiveOptions: { exact: true },
});
return true;
}
canDeactivate(
component: unknown,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot,
) {
if (!nextState.url.startsWith('/main/dossiers')) {
this._breadcrumbsService.showGoBack();
}
return true; return true;
} }
} }

View File

@ -1,16 +1,13 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanDeactivate, Router, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesMapService } from '@services/entity-services/files-map.service';
import { DossiersService } from '@services/entity-services/dossiers.service'; import { DossiersService } from '@services/entity-services/dossiers.service';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
import { pluck } from 'rxjs/operators';
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class FilePreviewGuard implements CanActivate, CanDeactivate<unknown> { export class FilePreviewGuard implements CanActivate {
constructor( constructor(
private readonly _filesMapService: FilesMapService, private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService, private readonly _dossiersService: DossiersService,
private readonly _breadcrumbsService: BreadcrumbsService,
private readonly _router: Router, private readonly _router: Router,
) {} ) {}
@ -25,32 +22,6 @@ export class FilePreviewGuard implements CanActivate, CanDeactivate<unknown> {
return false; return false;
} }
this._breadcrumbsService.append({
name$: this._filesMapService.watch$(dossierId, fileId).pipe(pluck('filename')),
routerLink: ['/main', 'dossiers', dossierId, 'file', fileId],
});
return true;
}
canDeactivate(
component: unknown,
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot,
nextState?: RouterStateSnapshot,
) {
const dossierId = currentRoute.paramMap.get('dossierId');
const fileId = currentRoute.paramMap.get('fileId');
this._breadcrumbsService.remove(['/main', 'dossiers', dossierId, 'file', fileId]);
if (!nextState.url.includes('dossiers/')) {
this._breadcrumbsService.remove(['/main', 'dossiers', dossierId]);
}
if (!nextState.url.startsWith('/main/dossiers')) {
this._breadcrumbsService.showGoBack();
}
return true; return true;
} }
} }

View File

@ -1,18 +0,0 @@
import { Injectable } from '@angular/core';
import { CanActivate, CanDeactivate } from '@angular/router';
import { BreadcrumbsService } from '@services/breadcrumbs.service';
@Injectable({ providedIn: 'root' })
export class GoBackGuard implements CanActivate, CanDeactivate<unknown> {
constructor(private readonly _breadcrumbsService: BreadcrumbsService) {}
canActivate(): boolean {
this._breadcrumbsService.showGoBack();
return true;
}
canDeactivate() {
this._breadcrumbsService.hideGoBack();
return true;
}
}

View File

@ -42,6 +42,8 @@ export class AnnotationWrapper {
legalBasisValue: string; legalBasisValue: string;
legalBasisChangeValue?: string; legalBasisChangeValue?: string;
resizing?: boolean; resizing?: boolean;
rectangle?: boolean;
section?: string;
manual?: boolean; manual?: boolean;
@ -217,6 +219,8 @@ export class AnnotationWrapper {
annotationWrapper.comments = redactionLogEntry.comments || []; annotationWrapper.comments = redactionLogEntry.comments || [];
annotationWrapper.manual = redactionLogEntry.manual; annotationWrapper.manual = redactionLogEntry.manual;
annotationWrapper.engines = redactionLogEntry.engines; annotationWrapper.engines = redactionLogEntry.engines;
annotationWrapper.section = redactionLogEntry.section;
annotationWrapper.rectangle = redactionLogEntry.rectangle;
this._createContent(annotationWrapper, redactionLogEntry); this._createContent(annotationWrapper, redactionLogEntry);
this._setSuperType(annotationWrapper, redactionLogEntry); this._setSuperType(annotationWrapper, redactionLogEntry);

View File

@ -13,7 +13,6 @@ export class ManualRedactionEntryWrapper {
readonly quads: any, readonly quads: any,
readonly manualRedactionEntry: IManualRedactionEntry, readonly manualRedactionEntry: IManualRedactionEntry,
readonly type: ManualRedactionEntryType, readonly type: ManualRedactionEntryType,
readonly annotationType: 'TEXT' | 'RECTANGLE' = 'TEXT',
readonly rectId?: string, readonly rectId?: string,
) {} ) {}
} }

View File

@ -10,6 +10,7 @@ export interface RedactionLogEntryWrapper {
reference?: Array<string>; reference?: Array<string>;
startOffset?: number; startOffset?: number;
type?: string; type?: string;
rectangle?: boolean;
color?: Array<number>; color?: Array<number>;
dictionaryEntry?: boolean; dictionaryEntry?: boolean;

View File

@ -5,6 +5,7 @@
[itemSize]="50" [itemSize]="50"
[noDataText]="'file-attributes-csv-import.no-data.title' | translate" [noDataText]="'file-attributes-csv-import.no-data.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="auto" emptyColumnWidth="auto"
noDataIcon="red:attribute" noDataIcon="red:attribute"
></iqser-table> ></iqser-table>

View File

@ -25,6 +25,7 @@
[itemSize]="80" [itemSize]="80"
[noDataIcon]="'iqser:document'" [noDataIcon]="'iqser:document'"
[noDataText]="'audit-screen.no-data.title' | translate" [noDataText]="'audit-screen.no-data.title' | translate"
[tableColumnConfigs]="tableColumnConfigs"
[totalSize]="logs?.totalHits || 0" [totalSize]="logs?.totalHits || 0"
> >
</iqser-table> </iqser-table>

View File

@ -20,7 +20,7 @@
<redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav> <redaction-admin-side-nav type="dossierTemplates"></redaction-admin-side-nav>
<div class="content-container"> <div class="content-container">
<iqser-table [itemSize]="80" emptyColumnWidth="2fr"></iqser-table> <iqser-table [itemSize]="80" [tableColumnConfigs]="tableColumnConfigs" emptyColumnWidth="2fr"></iqser-table>
</div> </div>
</div> </div>
</section> </section>

View File

@ -30,6 +30,7 @@
[noMatchText]="'dictionary-listing.no-match.title' | translate" [noMatchText]="'dictionary-listing.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[showNoDataButton]="currentUser.isAdmin" [showNoDataButton]="currentUser.isAdmin"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr" emptyColumnWidth="1fr"
noDataIcon="red:dictionary" noDataIcon="red:dictionary"
></iqser-table> ></iqser-table>

View File

@ -30,6 +30,7 @@
[noMatchText]="'dossier-attributes-listing.no-match.title' | translate" [noMatchText]="'dossier-attributes-listing.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[showNoDataButton]="currentUser.isAdmin" [showNoDataButton]="currentUser.isAdmin"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr" emptyColumnWidth="1fr"
noDataIcon="red:attribute" noDataIcon="red:attribute"
></iqser-table> ></iqser-table>

View File

@ -19,6 +19,7 @@
[noDataText]="'dossier-templates-listing.no-data.title' | translate" [noDataText]="'dossier-templates-listing.no-data.title' | translate"
[noMatchText]="'dossier-templates-listing.no-match.title' | translate" [noMatchText]="'dossier-templates-listing.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="red:template" noDataIcon="red:template"
></iqser-table> ></iqser-table>
</div> </div>

View File

@ -27,6 +27,7 @@
[noDataText]="'file-attributes-listing.no-data.title' | translate" [noDataText]="'file-attributes-listing.no-data.title' | translate"
[noMatchText]="'file-attributes-listing.no-match.title' | translate" [noMatchText]="'file-attributes-listing.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr" emptyColumnWidth="1fr"
noDataIcon="red:attribute" noDataIcon="red:attribute"
></iqser-table> ></iqser-table>

View File

@ -4,6 +4,7 @@
[itemSize]="80" [itemSize]="80"
[noDataText]="'justifications-listing.no-data.title' | translate" [noDataText]="'justifications-listing.no-data.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="iqser:document" noDataIcon="iqser:document"
></iqser-table> ></iqser-table>

View File

@ -15,6 +15,7 @@
[noDataText]="'trash.no-data.title' | translate" [noDataText]="'trash.no-data.title' | translate"
[noMatchText]="'trash.no-match.title' | translate" [noMatchText]="'trash.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableItemClasses]="{ disabled: disabledFn }" [tableItemClasses]="{ disabled: disabledFn }"
noDataIcon="red:template" noDataIcon="red:template"
></iqser-table> ></iqser-table>

View File

@ -37,6 +37,7 @@
[itemSize]="80" [itemSize]="80"
[noMatchText]="'user-listing.no-match.title' | translate" [noMatchText]="'user-listing.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
emptyColumnWidth="1fr" emptyColumnWidth="1fr"
></iqser-table> ></iqser-table>
</div> </div>

View File

@ -21,6 +21,16 @@
<input [value]="form.get('reason').value?.legalBasis" disabled type="text" /> <input [value]="form.get('reason').value?.legalBasis" disabled type="text" />
</div> </div>
<div class="iqser-input-group w-400">
<label translate="change-legal-basis-dialog.content.section"></label>
<input formControlName="section" name="section" type="text" />
</div>
<div class="iqser-input-group w-400" *ngIf="this.allRectangles">
<label translate="change-legal-basis-dialog.content.classification"></label>
<input formControlName="classification" name="classification" type="text" />
</div>
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300"> <div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<label translate="change-legal-basis-dialog.content.comment"></label> <label translate="change-legal-basis-dialog.content.comment"></label>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea> <textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>

View File

@ -31,7 +31,15 @@ export class ChangeLegalBasisDialogComponent implements OnInit {
) {} ) {}
get changed(): boolean { get changed(): boolean {
return this.form.get('reason').value.legalBasis !== this._data.annotations[0].legalBasis; return (
this.form.get('reason').value.legalBasis !== this._data.annotations[0].legalBasis ||
this.form.get('section').value !== this._data.annotations[0].section ||
this.form.get('classification').value !== this._data.annotations[0].value
);
}
get allRectangles(): boolean {
return this._data.annotations.reduce((acc, a) => acc && a.rectangle, true);
} }
async ngOnInit() { async ngOnInit() {
@ -55,13 +63,17 @@ export class ChangeLegalBasisDialogComponent implements OnInit {
return this._formBuilder.group({ return this._formBuilder.group({
reason: [null, Validators.required], reason: [null, Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required], comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
classification: [this.allRectangles ? this._data.annotations[0].value : null],
section: [this._data.annotations[0].section],
}); });
} }
save() { save() {
this.dialogRef.close({ this.dialogRef.close({
legalBasis: this.form.get('reason').value.legalBasis, legalBasis: this.form.get('reason').value.legalBasis,
section: this.form.get('section').value,
comment: this.form.get('comment').value, comment: this.form.get('comment').value,
value: this.form.get('classification').value,
}); });
} }
} }

View File

@ -4,6 +4,7 @@
[itemSize]="50" [itemSize]="50"
[noDataText]="'edit-dossier-dialog.deleted-documents.no-data.title' | translate" [noDataText]="'edit-dossier-dialog.deleted-documents.no-data.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableItemClasses]="{ disabled: disabledFn }" [tableItemClasses]="{ disabled: disabledFn }"
noDataIcon="iqser:document" noDataIcon="iqser:document"
></iqser-table> ></iqser-table>

View File

@ -124,17 +124,13 @@ export class EditDossierDialogComponent extends BaseDialogComponent {
return this.activeComponent?.disabled; return this.activeComponent?.disabled;
} }
afterSave() {
this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this._dossierName } });
}
async save(closeAfterSave: boolean = false) { async save(closeAfterSave: boolean = false) {
this._loadingService.start(); this._loadingService.start();
const result = await this.activeComponent.save(); const result = await this.activeComponent.save();
this._loadingService.stop(); this._loadingService.stop();
if (result.success) { if (result.success) {
this.afterSave(); this._toaster.success(_('edit-dossier-dialog.change-successful'), { params: { dossierName: this._dossierName } });
} }
if (result.success && closeAfterSave) { if (result.success && closeAfterSave) {

View File

@ -116,7 +116,6 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
}); });
this._dialogService.openDialog('confirm', null, data, async () => { this._dialogService.openDialog('confirm', null, data, async () => {
await this._dossiersService.delete(this.dossier).toPromise(); await this._dossiersService.delete(this.dossier).toPromise();
this._editDossierDialogRef.componentInstance.afterSave();
this._editDossierDialogRef.close(); this._editDossierDialogRef.close();
this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted()); this._router.navigate(['main', 'dossiers']).then(() => this._notifyDossierDeleted());
}); });

View File

@ -3,19 +3,37 @@
<div [translate]="title" class="dialog-header heading-l"></div> <div [translate]="title" class="dialog-header heading-l"></div>
<div class="dialog-content"> <div class="dialog-content">
<ng-container *ngIf="data.manualRedactionEntryWrapper.annotationType === 'TEXT'"> <ng-container *ngIf="!data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle">
<div class="iqser-input-group"> <div class="iqser-input-group">
<label translate="manual-annotation.dialog.content.text"></label> <label translate="manual-annotation.dialog.content.text"></label>
</div> </div>
{{ format(data.manualRedactionEntryWrapper.manualRedactionEntry.value) }} {{ format(data.manualRedactionEntryWrapper.manualRedactionEntry.value) }}
</ng-container> </ng-container>
<ng-container *ngIf="data.manualRedactionEntryWrapper.annotationType === 'RECTANGLE'"> <ng-container *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle">
<div class="iqser-input-group"> <div class="iqser-input-group">
<label translate="manual-annotation.dialog.content.rectangle"></label> <label translate="manual-annotation.dialog.content.rectangle"></label>
</div> </div>
</ng-container> </ng-container>
<div *ngIf="!isFalsePositiveRequest && isDictionaryRequest" class="iqser-input-group required w-400">
<label translate="manual-annotation.dialog.content.dictionary"></label>
<mat-select formControlName="dictionary">
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger>
<mat-option
*ngFor="let dictionary of possibleDictionaries"
[matTooltip]="dictionary.description"
[value]="dictionary.type"
matTooltipPosition="after"
>
<span>
{{ dictionary.label }}
</span>
</mat-option>
</mat-select>
</div>
<div *ngIf="!isDictionaryRequest" class="iqser-input-group required w-400"> <div *ngIf="!isDictionaryRequest" class="iqser-input-group required w-400">
<label translate="manual-annotation.dialog.content.reason"></label> <label translate="manual-annotation.dialog.content.reason"></label>
<mat-select <mat-select
@ -39,27 +57,19 @@
<input [value]="redactionForm.get('reason').value?.legalBasis" disabled type="text" /> <input [value]="redactionForm.get('reason').value?.legalBasis" disabled type="text" />
</div> </div>
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300"> <div *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle" class="iqser-input-group w-400">
<label translate="manual-annotation.dialog.content.comment"></label> <label translate="manual-annotation.dialog.content.section"></label>
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea> <input formControlName="section" name="section" type="text" />
</div> </div>
<div *ngIf="isDictionaryRequest && !isFalsePositiveRequest" class="iqser-input-group required w-300"> <div *ngIf="data.manualRedactionEntryWrapper.manualRedactionEntry.rectangle" class="iqser-input-group w-400">
<label translate="manual-annotation.dialog.content.dictionary"></label> <label translate="manual-annotation.dialog.content.classification"></label>
<input formControlName="classification" name="classification" type="text" />
</div>
<mat-select formControlName="dictionary"> <div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
<mat-select-trigger>{{ displayedDictionaryLabel }}</mat-select-trigger> <label translate="manual-annotation.dialog.content.comment"></label>
<mat-option <textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
*ngFor="let dictionary of redactionDictionaries"
[matTooltip]="dictionary.description"
[value]="dictionary.type"
matTooltipPosition="after"
>
<span>
{{ dictionary.label }}
</span>
</mat-option>
</mat-select>
</div> </div>
</div> </div>

View File

@ -28,7 +28,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
isDictionaryRequest: boolean; isDictionaryRequest: boolean;
isFalsePositiveRequest: boolean; isFalsePositiveRequest: boolean;
redactionDictionaries: Dictionary[] = []; possibleDictionaries: Dictionary[] = [];
legalOptions: LegalBasisOption[] = []; legalOptions: LegalBasisOption[] = [];
private readonly _dossier: Dossier; private readonly _dossier: Dossier;
@ -51,7 +51,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
this.redactionForm = this._getForm(); this.redactionForm = this._getForm();
this.redactionDictionaries = this._redactionDictionaries; this.possibleDictionaries = this._possibleDictionaries;
} }
get title() { get title() {
@ -61,24 +61,25 @@ export class ManualAnnotationDialogComponent implements OnInit {
get displayedDictionaryLabel() { get displayedDictionaryLabel() {
const dictType = this.redactionForm.get('dictionary').value; const dictType = this.redactionForm.get('dictionary').value;
if (dictType) { if (dictType) {
return this.redactionDictionaries.find(d => d.type === dictType).label; return this.possibleDictionaries.find(d => d.type === dictType).label;
} }
return null; return null;
} }
private get _redactionDictionaries(): Dictionary[] { private get _possibleDictionaries(): Dictionary[] {
const redactionDictionaries: Dictionary[] = []; const possibleDictionaries: Dictionary[] = [];
const dossier = this._dossier; const dossier = this._dossier;
for (const key of Object.keys(this._appStateService.dictionaryData[dossier.dossierTemplateId])) { for (const key of Object.keys(this._appStateService.dictionaryData[dossier.dossierTemplateId])) {
const dictionaryData = this._appStateService.getDictionary(key, dossier.dossierTemplateId); const dictionaryData = this._appStateService.getDictionary(key, dossier.dossierTemplateId);
if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) { if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) {
redactionDictionaries.push(dictionaryData); possibleDictionaries.push(dictionaryData);
} }
} }
redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label));
return redactionDictionaries; possibleDictionaries.sort((a, b) => a.label.localeCompare(b.label));
return possibleDictionaries;
} }
async ngOnInit() { async ngOnInit() {
@ -111,11 +112,13 @@ export class ManualAnnotationDialogComponent implements OnInit {
private _getForm(): FormGroup { private _getForm(): FormGroup {
return this._formBuilder.group({ return this._formBuilder.group({
section: [null],
reason: this.isDictionaryRequest ? [null] : [null, Validators.required], reason: this.isDictionaryRequest ? [null] : [null, Validators.required],
dictionary: this.isDictionaryRequest dictionary: this.isDictionaryRequest
? [this.isFalsePositiveRequest ? 'false_positive' : null, Validators.required] ? [this.isFalsePositiveRequest ? 'false_positive' : null, Validators.required]
: ['manual', Validators.required], : ['manual', Validators.required],
comment: this.isDocumentAdmin ? [null] : [null, Validators.required], comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
classification: ['non-readable content'],
}); });
} }
@ -133,5 +136,9 @@ export class ManualAnnotationDialogComponent implements OnInit {
} }
const commentValue = this.redactionForm.get('comment').value; const commentValue = this.redactionForm.get('comment').value;
addRedactionRequest.comment = commentValue ? { text: commentValue } : null; addRedactionRequest.comment = commentValue ? { text: commentValue } : null;
addRedactionRequest.section = this.redactionForm.get('section').value;
addRedactionRequest.value = addRedactionRequest.rectangle
? this.redactionForm.get('classification').value
: addRedactionRequest.value;
} }
} }

View File

@ -3,22 +3,20 @@ import { RouterModule, Routes } from '@angular/router';
import { SearchScreenComponent } from './screens/search-screen/search-screen.component'; import { SearchScreenComponent } from './screens/search-screen/search-screen.component';
import { FilePreviewGuard } from '@guards/file-preview.guard'; import { FilePreviewGuard } from '@guards/file-preview.guard';
import { DossierFilesGuard } from '@guards/dossier-files-guard'; import { DossierFilesGuard } from '@guards/dossier-files-guard';
import { GoBackGuard } from '@guards/go-back-guard.service';
import { CompositeRouteGuard } from '@iqser/common-ui'; import { CompositeRouteGuard } from '@iqser/common-ui';
import { BreadcrumbTypes } from '@red/domain';
const routes: Routes = [ const routes: Routes = [
{ {
path: 'search', path: 'search',
component: SearchScreenComponent, component: SearchScreenComponent,
canActivate: [GoBackGuard],
canDeactivate: [GoBackGuard],
}, },
{ {
path: ':dossierId', path: ':dossierId',
canActivate: [CompositeRouteGuard], canActivate: [CompositeRouteGuard],
canDeactivate: [DossierFilesGuard],
data: { data: {
routeGuards: [DossierFilesGuard], routeGuards: [DossierFilesGuard],
breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier],
}, },
loadChildren: () => import('./screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule), loadChildren: () => import('./screens/dossier-overview/dossier-overview.module').then(m => m.DossierOverviewModule),
}, },
@ -27,14 +25,15 @@ const routes: Routes = [
canActivate: [CompositeRouteGuard], canActivate: [CompositeRouteGuard],
data: { data: {
routeGuards: [DossierFilesGuard, FilePreviewGuard], routeGuards: [DossierFilesGuard, FilePreviewGuard],
breadcrumbs: [BreadcrumbTypes.main, BreadcrumbTypes.dossier, BreadcrumbTypes.file],
}, },
canDeactivate: [FilePreviewGuard],
loadChildren: () => import('./screens/file-preview-screen/file-preview.module').then(m => m.FilePreviewModule), loadChildren: () => import('./screens/file-preview-screen/file-preview.module').then(m => m.FilePreviewModule),
}, },
{ {
path: '', path: '',
pathMatch: 'full', pathMatch: 'full',
loadChildren: () => import('./screens/dossiers-listing/dossiers-listing.module').then(m => m.DossiersListingModule), loadChildren: () => import('./screens/dossiers-listing/dossiers-listing.module').then(m => m.DossiersListingModule),
data: { breadcrumbs: [BreadcrumbTypes.main] },
}, },
]; ];

View File

@ -23,6 +23,7 @@
[noMatchText]="'dossier-overview.no-match.title' | translate" [noMatchText]="'dossier-overview.no-match.title' | translate"
[selectionEnabled]="true" [selectionEnabled]="true"
[showNoDataButton]="true" [showNoDataButton]="true"
[tableColumnConfigs]="tableColumnConfigs"
[tableItemClasses]="{ disabled: disabledFn, 'last-opened': lastOpenedFn }" [tableItemClasses]="{ disabled: disabledFn, 'last-opened': lastOpenedFn }"
helpModeKey="document-list" helpModeKey="document-list"
></iqser-table> ></iqser-table>

View File

@ -17,7 +17,7 @@ import { FileUploadService } from '@upload-download/services/file-upload.service
import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; import { StatusOverlayService } from '@upload-download/services/status-overlay.service';
import * as moment from 'moment'; import * as moment from 'moment';
import { Observable, timer } from 'rxjs'; import { Observable, timer } from 'rxjs';
import { filter, switchMap, tap } from 'rxjs/operators'; import { filter, skip, switchMap, tap } from 'rxjs/operators';
import { convertFiles, Files, handleFileDrop } from '@utils/index'; import { convertFiles, Files, handleFileDrop } from '@utils/index';
import { import {
CircleButtonTypes, CircleButtonTypes,
@ -73,6 +73,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
private readonly _needsWorkFilterTemplate: TemplateRef<unknown>; private readonly _needsWorkFilterTemplate: TemplateRef<unknown>;
@ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef; @ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef;
@ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>; @ViewChild(TableComponent) private readonly _tableComponent: TableComponent<Dossier>;
private _fileAttributeConfigs: IFileAttributeConfig[];
constructor( constructor(
protected readonly _injector: Injector, protected readonly _injector: Injector,
@ -99,19 +100,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
.getEntityChanged$(this.dossierId) .getEntityChanged$(this.dossierId)
.pipe(tap(dossier => (this.dossierTemplateId = dossier.dossierTemplateId))); .pipe(tap(dossier => (this.dossierTemplateId = dossier.dossierTemplateId)));
this.currentDossier = this._dossiersService.find(this.dossierId); this.currentDossier = this._dossiersService.find(this.dossierId);
this._updateFileAttributes();
this.fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(
this.currentDossier.dossierTemplateId,
)?.fileAttributeConfigs;
this.tableColumnConfigs = this.configService.tableConfig(this.displayedAttributes);
}
private _fileAttributeConfigs: IFileAttributeConfig[];
set fileAttributeConfigs(value: IFileAttributeConfig[]) {
this._fileAttributeConfigs = value || [];
this.displayedInFileListAttributes = this._fileAttributeConfigs.filter(config => config.displayedInFileList);
this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
} }
get checkedRequiredFilters(): NestedFilter[] { get checkedRequiredFilters(): NestedFilter[] {
@ -123,6 +112,7 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
} }
disabledFn = (file: File) => file.excluded; disabledFn = (file: File) => file.excluded;
lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id; lastOpenedFn = (file: File) => this._userPreferenceService.getLastOpenedFileForDossier(file.dossierId) === file.id;
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
@ -150,11 +140,15 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
this._computeAllFilters(); this._computeAllFilters();
}); });
this.addSubscription = this._dossierTemplatesService.entityChanged$.subscribe(() => { this.addSubscription = this._dossierTemplatesService
this.fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig( .getEntityChanged$(this.currentDossier.dossierTemplateId)
this.currentDossier.dossierTemplateId, .pipe(
)?.fileAttributeConfigs; skip(1),
}); tap(() => {
this._updateFileAttributes();
}),
)
.subscribe();
try { try {
this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.currentDossier); this.dossierAttributes = await this._dossierAttributesService.getWithValues(this.currentDossier);
@ -170,14 +164,12 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
super.ngOnDestroy(); super.ngOnDestroy();
} }
async ngOnAttach() { ngOnAttach() {
await this.ngOnInit(); this._fileDropOverlayService.initFileDropHandling(this.dossierId);
this._tableComponent?.scrollToLastIndex(); this._tableComponent?.scrollToLastIndex();
} }
ngOnDetach() { ngOnDetach() {}
this.ngOnDestroy();
}
forceReanalysisAction($event: LongPressEvent) { forceReanalysisAction($event: LongPressEvent) {
this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled; this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled;
@ -203,6 +195,15 @@ export class DossierOverviewScreenComponent extends ListingComponent<File> imple
recentlyModifiedChecker = (file: File) => recentlyModifiedChecker = (file: File) =>
moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment()); moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment());
private _updateFileAttributes(): void {
this._fileAttributeConfigs =
this._fileAttributesService.getFileAttributeConfig(this.currentDossier.dossierTemplateId)?.fileAttributeConfigs || [];
this.displayedInFileListAttributes = this._fileAttributeConfigs.filter(config => config.displayedInFileList);
this.displayedAttributes = this.displayedInFileListAttributes.filter(c => c.displayedInFileList);
this.tableColumnConfigs = this.configService.tableConfig(this.displayedAttributes);
this._computeAllFilters();
}
private async _reloadFiles() { private async _reloadFiles() {
await this._filesService.loadAll(this.dossierId).toPromise(); await this._filesService.loadAll(this.dossierId).toPromise();
this._computeAllFilters(); this._computeAllFilters();

View File

@ -13,6 +13,7 @@
[noDataText]="'dossier-listing.no-data.title' | translate" [noDataText]="'dossier-listing.no-data.title' | translate"
[noMatchText]="'dossier-listing.no-match.title' | translate" [noMatchText]="'dossier-listing.no-match.title' | translate"
[showNoDataButton]="currentUser.isManager" [showNoDataButton]="currentUser.isManager"
[tableColumnConfigs]="tableColumnConfigs"
helpModeKey="dossier-list" helpModeKey="dossier-list"
noDataIcon="red:folder" noDataIcon="red:folder"
></iqser-table> ></iqser-table>

View File

@ -2,6 +2,7 @@
<div> <div>
<iqser-circle-button <iqser-circle-button
(action)="edit()" (action)="edit()"
*ngIf="permissionsService.canEditFileAttributes(dossier, file)"
[tooltip]="'file-preview.tabs.document-info.edit' | translate" [tooltip]="'file-preview.tabs.document-info.edit' | translate"
icon="iqser:edit" icon="iqser:edit"
tooltipPosition="before" tooltipPosition="before"

View File

@ -5,6 +5,7 @@ import { AutoUnsubscribe } from '@iqser/common-ui';
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service'; import { DocumentInfoService } from '../../services/document-info.service';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { PermissionsService } from '../../../../../../services/permissions.service';
@Component({ @Component({
selector: 'redaction-document-info [file] [dossier]', selector: 'redaction-document-info [file] [dossier]',
@ -22,6 +23,7 @@ export class DocumentInfoComponent extends AutoUnsubscribe implements OnInit {
constructor( constructor(
private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _dossierTemplatesService: DossierTemplatesService,
private readonly _dialogService: DossiersDialogService, private readonly _dialogService: DossiersDialogService,
readonly permissionsService: PermissionsService,
readonly documentInfoService: DocumentInfoService, readonly documentInfoService: DocumentInfoService,
) { ) {
super(); super();

View File

@ -240,7 +240,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
// this will auto select rectangle after drawing // this will auto select rectangle after drawing
if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') { if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') {
this.annotationManager.selectAnnotations(annotations); this.annotationManager.selectAnnotations(annotations);
annotations[0].enableRotationControl(); annotations[0].disableRotationControl();
} }
}); });
@ -495,12 +495,10 @@ export class PdfViewerComponent implements OnInit, OnChanges {
const activeAnnotation = this.annotationManager.getSelectedAnnotations()[0]; const activeAnnotation = this.annotationManager.getSelectedAnnotations()[0];
const activePage = activeAnnotation.getPageNumber(); const activePage = activeAnnotation.getPageNumber();
const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance)]; const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation, this.instance)];
const manualRedaction = this._getManualRedaction({ [activePage]: quads }, 'Rectangle'); const manualRedaction = this._getManualRedaction({ [activePage]: quads });
this._cleanUpSelectionAndButtonState(); this._cleanUpSelectionAndButtonState();
this.manualAnnotationRequested.emit( this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(quads, manualRedaction, 'REDACTION', activeAnnotation.Id));
new ManualRedactionEntryWrapper(quads, manualRedaction, 'REDACTION', 'RECTANGLE', activeAnnotation.Id),
);
} }
private _cleanUpSelectionAndButtonState() { private _cleanUpSelectionAndButtonState() {
@ -619,7 +617,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
private _getManualRedaction( private _getManualRedaction(
quads: Readonly<Record<string, Core.Math.Quad[]>>, quads: Readonly<Record<string, Core.Math.Quad[]>>,
text: string, text?: string,
convertQuads = false, convertQuads = false,
): IManualRedactionEntry { ): IManualRedactionEntry {
const entry: IManualRedactionEntry = { positions: [] }; const entry: IManualRedactionEntry = { positions: [] };
@ -633,6 +631,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
} }
entry.value = text; entry.value = text;
entry.rectangle = !text;
return entry; return entry;
} }

View File

@ -79,6 +79,7 @@
*ngIf="displayPdfViewer" *ngIf="displayPdfViewer"
[annotations]="annotations" [annotations]="annotations"
[canPerformActions]="canPerformAnnotationActions$ | async" [canPerformActions]="canPerformAnnotationActions$ | async"
[class.hidden]="!ready"
[dossier]="dossier" [dossier]="dossier"
[fileData]="fileData?.fileData" [fileData]="fileData?.fileData"
[file]="file" [file]="file"

View File

@ -62,3 +62,7 @@
max-width: 400px; max-width: 400px;
width: 400px; width: 400px;
} }
redaction-pdf-viewer.hidden {
visibility: hidden;
}

View File

@ -1,14 +1,4 @@
import { import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
HostListener,
NgZone,
OnDestroy,
OnInit,
TemplateRef,
ViewChild,
} from '@angular/core';
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router'; import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
import { AppStateService } from '@state/app-state.service'; import { AppStateService } from '@state/app-state.service';
import { Core, WebViewerInstance } from '@pdftron/webviewer'; import { Core, WebViewerInstance } from '@pdftron/webviewer';
@ -64,7 +54,6 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
templateUrl: './file-preview-screen.component.html', templateUrl: './file-preview-screen.component.html',
styleUrls: ['./file-preview-screen.component.scss'], styleUrls: ['./file-preview-screen.component.scss'],
providers: [FilterService, ExcludedPagesService, ViewModeService, MultiSelectService, DocumentInfoService], providers: [FilterService, ExcludedPagesService, ViewModeService, MultiSelectService, DocumentInfoService],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach { export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
@ -84,6 +73,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
readonly viewDocumentInfo$: Observable<boolean>; readonly viewDocumentInfo$: Observable<boolean>;
readonly file$: Observable<File>; readonly file$: Observable<File>;
readonly fileId: string; readonly fileId: string;
ready = false;
private _instance: WebViewerInstance; private _instance: WebViewerInstance;
private _lastPage: string; private _lastPage: string;
private _reloadFileOnReanalysis = false; private _reloadFileOnReanalysis = false;
@ -177,13 +167,16 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
async updateViewMode(): Promise<void> { async updateViewMode(): Promise<void> {
const annotations = this._getAnnotations(a => a.getCustomData('redacto-manager')); const ocrAnnotationIds = this.annotationData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
const annotations = this._getAnnotations(a => a.getCustomData('redact-manager'));
const redactions = annotations.filter(a => a.getCustomData('redaction')); const redactions = annotations.filter(a => a.getCustomData('redaction'));
switch (this.viewModeService.viewMode) { switch (this.viewModeService.viewMode) {
case 'STANDARD': { case 'STANDARD': {
this._setAnnotationsColor(redactions, 'annotationColor'); this._setAnnotationsColor(redactions, 'annotationColor');
const standardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'false'); const standardEntries = annotations
.filter(a => a.getCustomData('changeLogRemoved') === 'false')
.filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true'); const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true');
this._show(standardEntries); this._show(standardEntries);
this._hide(nonStandardEntries); this._hide(nonStandardEntries);
@ -225,10 +218,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
await this.ngOnInit(); await this.ngOnInit();
this._lastPage = previousRoute.queryParams.page; this._lastPage = previousRoute.queryParams.page;
this._changeDetectorRef.markForCheck(); this._changeDetectorRef.markForCheck();
this._loadingService.stop();
} }
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
this.ready = false;
this._loadingService.start(); this._loadingService.start();
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId); await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
this._subscribeToFileUpdates(); this._subscribeToFileUpdates();
@ -403,6 +396,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async viewerReady($event: WebViewerInstance) { async viewerReady($event: WebViewerInstance) {
this._instance = $event; this._instance = $event;
this.ready = true;
await this._stampPDF(); await this._stampPDF();
await this._cleanupAndRedrawManualAnnotations(); await this._cleanupAndRedrawManualAnnotations();
this._setExcludedPageStyles(); this._setExcludedPageStyles();

View File

@ -27,9 +27,9 @@ export class DocumentInfoService {
fileAttributes$(fileId: string, dossierId: string, dossierTemplateId: string) { fileAttributes$(fileId: string, dossierId: string, dossierTemplateId: string) {
const getAttributes = () => this._fileAttributesService.getFileAttributeConfig(dossierTemplateId).fileAttributeConfigs; const getAttributes = () => this._fileAttributesService.getFileAttributeConfig(dossierTemplateId).fileAttributeConfigs;
const dossierTemplateChange$ = this._dossierTemplatesService.entityChanged$.pipe( const dossierTemplateChange$ = this._dossierTemplatesService
filter(template => template.dossierTemplateId === dossierTemplateId), .getEntityChanged$(dossierTemplateId)
); .pipe(filter(template => template.dossierTemplateId === dossierTemplateId));
const fileChange$ = this._filesMapService.watch$(dossierId, fileId); const fileChange$ = this._filesMapService.watch$(dossierId, fileId);
return merge(dossierTemplateChange$, fileChange$).pipe( return merge(dossierTemplateChange$, fileChange$).pipe(
map(getAttributes), map(getAttributes),

View File

@ -16,6 +16,7 @@
[itemSize]="85" [itemSize]="85"
[noDataText]="'search-screen.no-data' | translate" [noDataText]="'search-screen.no-data' | translate"
[noMatchText]="'search-screen.no-match' | translate" [noMatchText]="'search-screen.no-match' | translate"
[tableColumnConfigs]="tableColumnConfigs"
noDataIcon="iqser:search" noDataIcon="iqser:search"
></iqser-table> ></iqser-table>
</div> </div>
@ -55,6 +56,10 @@
</div> </div>
</div> </div>
<div class="cell">
<redaction-initials-avatar [user]="item.assignee" [withName]="true"></redaction-initials-avatar>
</div>
<div class="cell"> <div class="cell">
<iqser-status-bar <iqser-status-bar
[configs]="[ [configs]="[

View File

@ -35,6 +35,7 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
readonly tableHeaderLabel = _('search-screen.table-header'); readonly tableHeaderLabel = _('search-screen.table-header');
readonly tableColumnConfigs: TableColumnConfig<ISearchListItem>[] = [ readonly tableColumnConfigs: TableColumnConfig<ISearchListItem>[] = [
{ label: _('search-screen.cols.document'), width: '2fr' }, { label: _('search-screen.cols.document'), width: '2fr' },
{ label: _('search-screen.cols.assignee') },
{ label: _('search-screen.cols.status') }, { label: _('search-screen.cols.status') },
{ label: _('search-screen.cols.dossier') }, { label: _('search-screen.cols.dossier') },
{ label: _('search-screen.cols.pages'), width: 'auto' }, { label: _('search-screen.cols.pages'), width: 'auto' },
@ -134,6 +135,7 @@ export class SearchScreenComponent extends ListingComponent<ISearchListItem> imp
unmatched: unmatchedTerms || null, unmatched: unmatchedTerms || null,
highlights, highlights,
status: file.workflowStatus, status: file.workflowStatus,
assignee: file.assignee,
numberOfPages: file.numberOfPages, numberOfPages: file.numberOfPages,
dossierName: this._dossiersService.find(dossierId).dossierName, dossierName: this._dossiersService.find(dossierId).dossierName,
filename: file.filename, filename: file.filename,

View File

@ -88,10 +88,17 @@ export class AnnotationActionsService {
'changeLegalBasis', 'changeLegalBasis',
$event, $event,
{ annotations, dossier: this._dossier(file) }, { annotations, dossier: this._dossier(file) },
(data: { comment: string; legalBasis: string }) => { (data: { comment: string; legalBasis: string; section: string; value: string }) => {
annotations.forEach(annotation => { annotations.forEach(annotation => {
this._processObsAndEmit( this._processObsAndEmit(
this._manualAnnotationService.changeLegalBasis(annotation.annotationId, file, data.legalBasis, data.comment), this._manualAnnotationService.changeLegalBasis(
annotation.annotationId,
file,
data.section,
data.value,
data.legalBasis,
data.comment,
),
annotation, annotation,
annotationsChanged, annotationsChanged,
); );
@ -383,7 +390,7 @@ export class AnnotationActionsService {
} }
updateHiddenAnnotation(annotations: AnnotationWrapper[], viewerAnnotations: Annotation[], hidden: boolean) { updateHiddenAnnotation(annotations: AnnotationWrapper[], viewerAnnotations: Annotation[], hidden: boolean) {
const annotationId = (viewerAnnotations[0] as any).Dx; const annotationId = viewerAnnotations[0].Id;
const annotationToBeUpdated = annotations.find((a: AnnotationWrapper) => a.annotationId === annotationId); const annotationToBeUpdated = annotations.find((a: AnnotationWrapper) => a.annotationId === annotationId);
annotationToBeUpdated.hidden = hidden; annotationToBeUpdated.hidden = hidden;
} }

View File

@ -180,7 +180,7 @@ export class AnnotationDrawService {
(hideSkipped && annotationWrapper.isSkipped) || (hideSkipped && annotationWrapper.isSkipped) ||
annotationWrapper.isOCR || annotationWrapper.isOCR ||
annotationWrapper.hidden; annotationWrapper.hidden;
annotation.setCustomData('redacto-manager', 'true'); annotation.setCustomData('redact-manager', 'true');
annotation.setCustomData('redaction', String(annotationWrapper.isRedacted)); annotation.setCustomData('redaction', String(annotationWrapper.isRedacted));
annotation.setCustomData('skipped', String(annotationWrapper.isSkipped)); annotation.setCustomData('skipped', String(annotationWrapper.isSkipped));
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry)); annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));

View File

@ -102,11 +102,11 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
} }
// /manualRedaction/request/legalBasis // /manualRedaction/request/legalBasis
changeLegalBasis(annotationId: string, file: File, legalBasis: string, comment?: string) { changeLegalBasis(annotationId: string, file: File, section: string, value: string, legalBasis: string, comment?: string) {
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file))
? 'change-legal-basis' ? 'change-legal-basis'
: 'request-change-legal-basis'; : 'request-change-legal-basis';
return this._makeRequest(mode, file, { annotationId, legalBasis, comment }); return this._makeRequest(mode, file, { annotationId, legalBasis, comment, section, value });
} }
// this wraps // this wraps

View File

@ -21,7 +21,6 @@ import {
ConfirmationDialogInput, ConfirmationDialogInput,
IqserTooltipPosition, IqserTooltipPosition,
LoadingService, LoadingService,
Required,
Toaster, Toaster,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -40,7 +39,7 @@ import { DocumentInfoService } from '../../../screens/file-preview-screen/servic
import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component'; import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component';
@Component({ @Component({
selector: 'redaction-file-actions', selector: 'redaction-file-actions [file] [type]',
templateUrl: './file-actions.component.html', templateUrl: './file-actions.component.html',
styleUrls: ['./file-actions.component.scss'], styleUrls: ['./file-actions.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
@ -49,8 +48,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser; readonly currentUser = this._userService.currentUser;
@Input() @Required() file: File; @Input() file: File;
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow'; @Input() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
@Input() maxWidth: number; @Input() maxWidth: number;
@Output() readonly ocredFile = new EventEmitter<void>(); @Output() readonly ocredFile = new EventEmitter<void>();
@ -58,28 +57,30 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
assignTooltip?: string; assignTooltip?: string;
buttonType?: CircleButtonType; buttonType?: CircleButtonType;
showUndoApproval: boolean; showUndoApproval = false;
showAssignToSelf: boolean; showAssignToSelf = false;
showAssign: boolean; showAssign = false;
showDelete: boolean; showDelete = false;
showOCR: boolean; showOCR = false;
canReanalyse: boolean; canReanalyse = false;
showUnderReview: boolean; showUnderReview = false;
showUnderApproval: boolean; showUnderApproval = false;
showApprove: boolean; showApprove = false;
canToggleAnalysis: boolean; canToggleAnalysis = false;
showStatusBar: boolean; showStatusBar = false;
showOpenDocument: boolean; showOpenDocument = false;
showReanalyseFilePreview: boolean; showReanalyseFilePreview = false;
showReanalyseDossierOverview: boolean; showReanalyseDossierOverview = false;
analysisForced: boolean; analysisForced = false;
isDossierOverview = false; isDossierOverview = false;
isDossierOverviewList = false; isDossierOverviewList = false;
isDossierOverviewWorkflow = false; isDossierOverviewWorkflow = false;
isFilePreview = false; isFilePreview = false;
tooltipPosition: IqserTooltipPosition; tooltipPosition: IqserTooltipPosition;
buttons: Action[]; buttons: Action[];
@ViewChild(ExpandableFileActionsComponent) _expandableActionsComponent: ExpandableFileActionsComponent;
@ViewChild(ExpandableFileActionsComponent)
private readonly _expandableActionsComponent: ExpandableFileActionsComponent;
constructor( constructor(
@Optional() private readonly _excludedPagesService: ExcludedPagesService, @Optional() private readonly _excludedPagesService: ExcludedPagesService,
@ -100,8 +101,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
super(); super();
} }
@HostBinding('class.keep-visible') get expanded() { @HostBinding('class.keep-visible')
return this._expandableActionsComponent?.expanded; get expanded() {
return !!this._expandableActionsComponent?.expanded;
} }
private get _toggleTooltip(): string { private get _toggleTooltip(): string {

View File

@ -1,7 +1,12 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { List, shareDistinctLast } from '@iqser/common-ui'; import { List } from '@iqser/common-ui';
import { IsActiveMatchOptions } from '@angular/router'; import { ActivatedRouteSnapshot, IsActiveMatchOptions, NavigationEnd, Router } from '@angular/router';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable, of } from 'rxjs';
import { filter, pluck } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { TranslateService } from '@ngx-translate/core';
import { BreadcrumbTypes } from '@red/domain';
export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions; export type RouterLinkActiveOptions = { exact: boolean } | IsActiveMatchOptions;
@ -18,40 +23,79 @@ export type Breadcrumbs = List<Breadcrumb>;
}) })
export class BreadcrumbsService { export class BreadcrumbsService {
readonly breadcrumbs$: Observable<Breadcrumbs>; readonly breadcrumbs$: Observable<Breadcrumbs>;
readonly showGoBack$: Observable<boolean>;
private readonly _showGoBack$ = new BehaviorSubject<boolean>(false);
private readonly _store$ = new BehaviorSubject<Breadcrumbs>([]); private readonly _store$ = new BehaviorSubject<Breadcrumbs>([]);
constructor() { constructor(
private readonly _router: Router,
private readonly _translateService: TranslateService,
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
) {
this.breadcrumbs$ = this._store$.asObservable(); this.breadcrumbs$ = this._store$.asObservable();
this.showGoBack$ = this._showGoBack$.asObservable().pipe(shareDistinctLast());
this._router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe(() => {
const root = this._router.routerState.snapshot.root;
this._clear();
this._addBreadcrumbs(root.firstChild);
});
} }
get breadcrumbs() { get breadcrumbs() {
return this._store$.value; return this._store$.value;
} }
append(breadcrumb: Breadcrumb) { private _append(breadcrumb: Breadcrumb) {
const existing = this._store$.value.find(item => item.routerLink.toString() === breadcrumb.routerLink.toString());
if (existing) {
this.remove(existing.routerLink);
}
this._store$.next([...this._store$.value, breadcrumb]); this._store$.next([...this._store$.value, breadcrumb]);
} }
clear() { private _clear() {
this._store$.next([]); this._store$.next([]);
} }
remove(routerLink: string[]) { private _addBreadcrumbs(route: ActivatedRouteSnapshot) {
this._store$.next(this._store$.value.filter(item => item.routerLink.toString() !== routerLink.toString())); if (route.firstChild) {
this._addBreadcrumbs(route.firstChild);
return;
}
for (const breadcrumb of route.data.breadcrumbs || []) {
switch (breadcrumb) {
case BreadcrumbTypes.main:
this._addMainBreadcrumb();
break;
case BreadcrumbTypes.dossier:
this._addDossierBreadcrumb(route);
break;
case BreadcrumbTypes.file:
this._addFileBreadcrumb(route);
break;
}
}
} }
showGoBack() { private _addMainBreadcrumb(): void {
this._showGoBack$.next(true); this._append({
name$: of(this._translateService.instant('top-bar.navigation-items.dossiers')),
routerLink: ['/main', 'dossiers'],
routerLinkActiveOptions: { exact: true },
});
} }
hideGoBack() { private _addDossierBreadcrumb(route: ActivatedRouteSnapshot): void {
this._showGoBack$.next(false); const dossierId = route.paramMap.get('dossierId');
this._append({
name$: this._dossiersService.getEntityChanged$(dossierId).pipe(pluck('dossierName')),
routerLink: ['/main', 'dossiers', dossierId],
routerLinkActiveOptions: { exact: true },
});
}
private _addFileBreadcrumb(route: ActivatedRouteSnapshot): void {
const dossierId = route.paramMap.get('dossierId');
const fileId = route.paramMap.get('fileId');
this._append({
name$: this._filesMapService.watch$(dossierId, fileId).pipe(pluck('filename')),
routerLink: ['/main', 'dossiers', dossierId, 'file', fileId],
});
} }
} }

View File

@ -70,8 +70,11 @@ export class FilesMapService {
} }
replace(entity: File) { replace(entity: File) {
const all = this.get(entity.dossierId).filter(file => file.fileId !== entity.fileId); const existingFile = this.get(entity.dossierId).find(file => file.fileId === entity.fileId);
this.set(entity.dossierId, [...all, entity]); if (existingFile.lastUpdated !== entity.lastUpdated) {
const all = this.get(entity.dossierId).filter(file => file.fileId !== entity.fileId);
this.set(entity.dossierId, [...all, entity]);
}
} }
watch$(key: string, entityId: string): Observable<File> { watch$(key: string, entityId: string): Observable<File> {

View File

@ -2,7 +2,6 @@ import { Injectable, Injector } from '@angular/core';
import { GenericService, RequiredParam, Validate } from '@iqser/common-ui'; import { GenericService, RequiredParam, Validate } from '@iqser/common-ui';
import { IGeneralConfiguration } from '@red/domain'; import { IGeneralConfiguration } from '@red/domain';
import { UserService } from '@services/user.service'; import { UserService } from '@services/user.service';
import { of } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -13,7 +12,7 @@ export class GeneralSettingsService extends GenericService<IGeneralConfiguration
} }
getGeneralConfigurations() { getGeneralConfigurations() {
return this._userService.currentUser?.hasAnyREDRoles ? this._getOne(['general']) : of({ displayName: 'RedactManager' }); return this._getOne(['general']);
} }
@Validate() @Validate()

View File

@ -18,8 +18,12 @@ export class PermissionsService {
return this.isApprover(dossier); return this.isApprover(dossier);
} }
canEditFileAttributes(dossier: Dossier, file: File): boolean {
return ((file.isUnderReview || file.isNew) && this.isDossierMember(dossier)) || (file.isUnderApproval && this.isApprover(dossier));
}
canToggleAnalysis(file: File): boolean { canToggleAnalysis(file: File): boolean {
return this.isReviewerOrApprover(file) && (file.isNew || file.isUnderReview || file.isUnderApproval); return this.isFileAssignee(file) && (file.isNew || file.isUnderReview || file.isUnderApproval);
} }
canReanalyseFile(file: File | File[]): boolean { canReanalyseFile(file: File | File[]): boolean {

View File

@ -1,7 +1,7 @@
import { catchError, filter, mergeMapTo, take, tap } from 'rxjs/operators'; import { catchError, filter, mergeMapTo, switchMap, take, tap } from 'rxjs/operators';
import { ConfigService } from '@services/config.service'; import { ConfigService } from '@services/config.service';
import { Title } from '@angular/platform-browser'; import { Title } from '@angular/platform-browser';
import { of } from 'rxjs'; import { from, of, throwError } from 'rxjs';
import { KeycloakEventType, KeycloakService } from 'keycloak-angular'; import { KeycloakEventType, KeycloakService } from 'keycloak-angular';
import { GeneralSettingsService } from '@services/general-settings.service'; import { GeneralSettingsService } from '@services/general-settings.service';
import { LanguageService } from '@i18n/language.service'; import { LanguageService } from '@i18n/language.service';
@ -19,14 +19,16 @@ export function configurationInitializer(
keycloakService.keycloakEvents$ keycloakService.keycloakEvents$
.pipe( .pipe(
filter(event => event.type === KeycloakEventType.OnReady), filter(event => event.type === KeycloakEventType.OnReady),
switchMap(() => from(keycloakService.isLoggedIn())),
switchMap(loggedIn => (!loggedIn ? throwError('Not Logged In') : of({}))),
mergeMapTo(generalSettingsService.getGeneralConfigurations()), mergeMapTo(generalSettingsService.getGeneralConfigurations()),
tap(configuration => configService.updateDisplayName(configuration.displayName)), tap(configuration => configService.updateDisplayName(configuration.displayName)),
tap(() => userPreferenceService.reload()),
tap(() => languageService.chooseAndSetInitialLanguage()),
catchError(() => { catchError(() => {
title.setTitle('RedactManager'); title.setTitle('RedactManager');
return of({}); return of({});
}), }),
tap(() => userPreferenceService.reload()),
tap(() => languageService.chooseAndSetInitialLanguage()),
take(1), take(1),
) )
.toPromise(); .toPromise();

View File

@ -41,7 +41,7 @@ export function getFirstRelevantTextPart(text, direction: 'FORWARD' | 'BACKWARD'
accumulator += char; accumulator += char;
return spaceCount >= 2; return spaceCount >= 3;
}; };
if (direction === 'FORWARD') { if (direction === 'FORWARD') {

View File

@ -359,6 +359,8 @@
"comment": "Comment", "comment": "Comment",
"legalBasis": "Legal Basis", "legalBasis": "Legal Basis",
"reason": "Select redaction reason", "reason": "Select redaction reason",
"section": "Paragraph / Location",
"classification": "Value / Classification",
"reason-placeholder": "Select a reason..." "reason-placeholder": "Select a reason..."
}, },
"header": "Edit Redaction Reason" "header": "Edit Redaction Reason"
@ -1258,6 +1260,8 @@
"dictionary": "Dictionary", "dictionary": "Dictionary",
"legalBasis": "Legal Basis", "legalBasis": "Legal Basis",
"reason": "Reason", "reason": "Reason",
"section": "Paragraph / Location",
"classification": "Value / Classification",
"reason-placeholder": "Select a reason ...", "reason-placeholder": "Select a reason ...",
"rectangle": "Custom Rectangle", "rectangle": "Custom Rectangle",
"text": "Selected text:" "text": "Selected text:"
@ -1488,6 +1492,7 @@
"cols": { "cols": {
"document": "Document", "document": "Document",
"dossier": "Dossier", "dossier": "Dossier",
"assignee": "Assignee",
"pages": "Pages", "pages": "Pages",
"status": "Status" "status": "Status"
}, },

@ -1 +1 @@
Subproject commit a82a08cd89e33325a789f5b433cd80adc8aa5e59 Subproject commit caf4838be63574740e5380c74ad96fe21f7a456b

View File

@ -104,7 +104,7 @@ export class File extends Entity<IFile> implements IFile {
this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW; this.isNew = this.workflowStatus === WorkflowFileStatuses.NEW;
this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW; this.isUnderReview = this.workflowStatus === WorkflowFileStatuses.UNDER_REVIEW;
this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL; this.isUnderApproval = this.workflowStatus === WorkflowFileStatuses.UNDER_APPROVAL;
this.canBeApproved = !this.analysisRequired && !this.hasSuggestions; this.canBeApproved = !this.analysisRequired && !this.hasSuggestions && !this.isProcessing && !this.isError;
this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0; this.canBeOpened = !this.isError && !this.isPending && this.numberOfAnalyses > 0;
this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isNew || this.isUnderReview || this.isUnderApproval); this.canBeOCRed = !this.excluded && !this.lastOCRTime && (this.isNew || this.isUnderReview || this.isUnderApproval);

View File

@ -10,4 +10,6 @@ export interface IAddRedactionRequest {
reason?: string; reason?: string;
type?: string; type?: string;
value?: string; value?: string;
section?: string;
rectangle?: boolean;
} }

View File

@ -16,4 +16,5 @@ export interface IManualRedactionEntry {
type?: string; type?: string;
user?: string; user?: string;
value?: string; value?: string;
rectangle?: boolean;
} }

View File

@ -14,6 +14,7 @@ export interface IRedactionLogEntry {
engines?: List<LogEntryEngine>; engines?: List<LogEntryEngine>;
excluded?: boolean; excluded?: boolean;
hint?: boolean; hint?: boolean;
rectangle?: boolean;
id?: string; id?: string;
image?: boolean; image?: boolean;
imageHasTransparency?: boolean; imageHasTransparency?: boolean;

View File

@ -3,6 +3,7 @@ import { IListable, List } from '@iqser/common-ui';
export interface ISearchListItem extends IListable { export interface ISearchListItem extends IListable {
readonly dossierId: string; readonly dossierId: string;
readonly filename: string; readonly filename: string;
readonly assignee: string;
readonly unmatched: List | null; readonly unmatched: List | null;
readonly highlights: Record<string, List>; readonly highlights: Record<string, List>;
readonly routerLink: string; readonly routerLink: string;

View File

@ -0,0 +1,7 @@
export type BreadcrumbType = 'main' | 'dossier' | 'file';
export const BreadcrumbTypes = {
main: 'main' as BreadcrumbType,
dossier: 'dossier' as BreadcrumbType,
file: 'file' as BreadcrumbType,
};

View File

@ -1,4 +1,5 @@
export * from './sorters/status-sorter'; export * from './sorters/status-sorter';
export * from './breadcrumb-types';
export * from './types'; export * from './types';
export * from './rules'; export * from './rules';
export * from './watermark'; export * from './watermark';

View File

@ -1,6 +1,6 @@
{ {
"name": "redaction", "name": "redaction",
"version": "3.103.0", "version": "3.105.0",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {

Binary file not shown.