Merge branch 'master' into VM/RED-2614
This commit is contained in:
commit
1831d17270
@ -36,6 +36,9 @@ import { KeycloakService } from 'keycloak-angular';
|
||||
import { GeneralSettingsService } from '@services/general-settings.service';
|
||||
import { BreadcrumbsComponent } from '@components/breadcrumbs/breadcrumbs.component';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import * as german from '../assets/i18n/de.json';
|
||||
import * as english from '../assets/i18n/en.json';
|
||||
import { UserService } from '@services/user.service';
|
||||
|
||||
export function httpLoaderFactory(httpClient: HttpClient): PruningTranslationLoader {
|
||||
return new PruningTranslationLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -114,7 +117,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
provide: APP_INITIALIZER,
|
||||
multi: true,
|
||||
useFactory: configurationInitializer,
|
||||
deps: [KeycloakService, Title, ConfigService, GeneralSettingsService, LanguageService, UserPreferenceService],
|
||||
deps: [KeycloakService, Title, ConfigService, GeneralSettingsService, LanguageService, UserService, UserPreferenceService],
|
||||
},
|
||||
{
|
||||
provide: MissingTranslationHandler,
|
||||
@ -136,6 +139,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
export class AppModule {
|
||||
constructor(private readonly _router: Router, private readonly _route: ActivatedRoute) {
|
||||
this._configureKeyCloakRouteHandling();
|
||||
// this._test();
|
||||
}
|
||||
|
||||
private _configureKeyCloakRouteHandling() {
|
||||
@ -152,4 +156,60 @@ export class AppModule {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// private _test(){
|
||||
//
|
||||
// const flatGerman = flatten(german);
|
||||
//
|
||||
//
|
||||
// const flatEnglish = flatten(english);
|
||||
//
|
||||
// const tmfc = new TranslateMessageFormatCompiler();
|
||||
//
|
||||
//
|
||||
//
|
||||
// for (const key of Object.keys(flatGerman)) {
|
||||
// try {
|
||||
// const result = tmfc.compile(flatGerman[key], 'de');
|
||||
// //console.log(result);
|
||||
// } catch (e) {
|
||||
// console.error('ERROR AT: ', flatGerman[key]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (const key of Object.keys(flatEnglish)) {
|
||||
// try {
|
||||
// const result = tmfc.compile(flatEnglish[key], 'de');
|
||||
// //console.log(result);
|
||||
// } catch (e) {
|
||||
// console.error('ERROR AT: ', flatEnglish[key]);
|
||||
// }
|
||||
// }
|
||||
// console.log('done');
|
||||
// }
|
||||
}
|
||||
|
||||
//
|
||||
// function flatten(data: any) {
|
||||
// const result: any = {};
|
||||
//
|
||||
// function recurse(cur: any, prop: any) {
|
||||
// if (Object(cur) !== cur) {
|
||||
// result[prop] = cur;
|
||||
// } else if (Array.isArray(cur)) {
|
||||
// let l = 0;
|
||||
// for (let i = 0, l = cur.length; i < l; i++) recurse(cur[i], prop + '[' + i + ']');
|
||||
// if (l === 0) result[prop] = [];
|
||||
// } else {
|
||||
// let isEmpty = true;
|
||||
// for (const p in cur) {
|
||||
// isEmpty = false;
|
||||
// recurse(cur[p], prop ? prop + '.' + p : p);
|
||||
// }
|
||||
// if (isEmpty && prop) result[prop] = {};
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// recurse(data, '');
|
||||
// return result;
|
||||
// }
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
@use 'variables';
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
::ng-deep .notifications-backdrop + .cdk-overlay-connected-position-bounding-box {
|
||||
right: 0 !important;
|
||||
|
||||
@ -20,6 +16,7 @@
|
||||
|
||||
.view-all {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--iqser-primary);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import { UserPreferenceService } from '@services/user-preference.service';
|
||||
export class LanguageService {
|
||||
constructor(private readonly _translateService: TranslateService, private readonly _userPreferenceService: UserPreferenceService) {
|
||||
_translateService.addLangs(['en', 'de']);
|
||||
_translateService.setDefaultLang('en');
|
||||
}
|
||||
|
||||
get currentLanguage() {
|
||||
|
||||
@ -14,6 +14,7 @@ export class AnnotationPermissions {
|
||||
canChangeLegalBasis = true;
|
||||
canResizeAnnotation = true;
|
||||
canRecategorizeImage = true;
|
||||
canForceHint = true;
|
||||
|
||||
static forUser(isApprover: boolean, user: User, annotations: AnnotationWrapper | AnnotationWrapper[]) {
|
||||
if (!isArray(annotations)) {
|
||||
@ -29,12 +30,13 @@ export class AnnotationPermissions {
|
||||
permissions.canAcceptSuggestion = isApprover && (annotation.isSuggestion || annotation.isDeclinedSuggestion);
|
||||
permissions.canRejectSuggestion = isApprover && annotation.isSuggestion;
|
||||
|
||||
permissions.canForceHint = annotation.isIgnoredHint;
|
||||
permissions.canForceRedaction = annotation.isSkipped && !annotation.isFalsePositive;
|
||||
permissions.canAcceptRecommendation = annotation.isRecommendation;
|
||||
|
||||
permissions.canMarkAsFalsePositive = annotation.canBeMarkedAsFalsePositive;
|
||||
|
||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted;
|
||||
permissions.canRemoveOrSuggestToRemoveOnlyHere = annotation.isRedacted || annotation.isHint;
|
||||
permissions.canRemoveOrSuggestToRemoveFromDictionary =
|
||||
annotation.isModifyDictionary && (annotation.isRedacted || annotation.isSkipped || annotation.isHint);
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ export type AnnotationSuperType =
|
||||
| 'suggestion-remove-dictionary'
|
||||
| 'suggestion-add'
|
||||
| 'suggestion-remove'
|
||||
| 'ignored-hint'
|
||||
| 'skipped'
|
||||
| 'redaction'
|
||||
| 'manual-redaction'
|
||||
@ -43,6 +44,7 @@ export class AnnotationWrapper {
|
||||
legalBasisChangeValue?: string;
|
||||
resizing?: boolean;
|
||||
rectangle?: boolean;
|
||||
hintDictionary?: boolean;
|
||||
section?: string;
|
||||
|
||||
manual?: boolean;
|
||||
@ -87,7 +89,7 @@ export class AnnotationWrapper {
|
||||
}
|
||||
|
||||
get isSuperTypeBasedColor() {
|
||||
return this.isSkipped || this.isSuggestion || this.isDeclinedSuggestion;
|
||||
return this.isSkipped || this.isSuggestion || this.isDeclinedSuggestion || this.isIgnoredHint;
|
||||
}
|
||||
|
||||
get isSkipped() {
|
||||
@ -106,6 +108,20 @@ export class AnnotationWrapper {
|
||||
return this.recategorizationType || this.typeValue;
|
||||
}
|
||||
|
||||
get topLevelFilter() {
|
||||
return (
|
||||
this.superType !== 'hint' &&
|
||||
this.superType !== 'redaction' &&
|
||||
this.superType !== 'recommendation' &&
|
||||
this.superType !== 'ignored-hint' &&
|
||||
this.superType !== 'skipped'
|
||||
);
|
||||
}
|
||||
|
||||
get filterKey() {
|
||||
return this.topLevelFilter ? this.superType : this.superType + this.type;
|
||||
}
|
||||
|
||||
get isManuallySkipped() {
|
||||
return this.isSkipped && this.manual;
|
||||
}
|
||||
@ -113,7 +129,10 @@ export class AnnotationWrapper {
|
||||
get isFalsePositive() {
|
||||
return (
|
||||
this.type?.toLowerCase() === 'false_positive' &&
|
||||
(this.superType === 'skipped' || this.superType === 'hint' || this.superType === 'redaction')
|
||||
(this.superType === 'skipped' ||
|
||||
this.superType === 'hint' ||
|
||||
this.superType === 'ignored-hint' ||
|
||||
this.superType === 'redaction')
|
||||
);
|
||||
}
|
||||
|
||||
@ -133,6 +152,10 @@ export class AnnotationWrapper {
|
||||
return this.superType === 'hint';
|
||||
}
|
||||
|
||||
get isIgnoredHint() {
|
||||
return this.superType === 'ignored-hint';
|
||||
}
|
||||
|
||||
get isRedacted() {
|
||||
return this.superType === 'redaction' || this.superType === 'manual-redaction';
|
||||
}
|
||||
@ -242,6 +265,7 @@ export class AnnotationWrapper {
|
||||
annotationWrapper.hasLegalBasisChanged = redactionLogEntry.hasLegalBasisChanged;
|
||||
annotationWrapper.hasBeenForced = redactionLogEntry.hasBeenForced;
|
||||
annotationWrapper.hasBeenRemovedByManualOverride = redactionLogEntry.hasBeenRemovedByManualOverride;
|
||||
annotationWrapper.hintDictionary = redactionLogEntry.hintDictionary;
|
||||
|
||||
this._createContent(annotationWrapper, redactionLogEntry);
|
||||
this._setSuperType(annotationWrapper, redactionLogEntry);
|
||||
@ -280,7 +304,7 @@ export class AnnotationWrapper {
|
||||
if (redactionLogEntryWrapper.status === 'REQUESTED') {
|
||||
annotationWrapper.superType = 'suggestion-force-redaction';
|
||||
} else if (redactionLogEntryWrapper.status === 'APPROVED') {
|
||||
annotationWrapper.superType = 'redaction';
|
||||
annotationWrapper.superType = redactionLogEntryWrapper.hint ? 'hint' : 'redaction';
|
||||
} else {
|
||||
annotationWrapper.superType = 'skipped';
|
||||
}
|
||||
@ -408,6 +432,11 @@ export class AnnotationWrapper {
|
||||
if (!annotationWrapper.superType) {
|
||||
annotationWrapper.superType = annotationWrapper.redaction ? 'redaction' : annotationWrapper.hint ? 'hint' : 'skipped';
|
||||
}
|
||||
if (annotationWrapper.superType === 'skipped') {
|
||||
if (redactionLogEntryWrapper.hintDictionary) {
|
||||
annotationWrapper.superType = 'ignored-hint';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static _createContent(annotationWrapper: AnnotationWrapper, entry: RedactionLogEntryWrapper) {
|
||||
|
||||
@ -3,34 +3,34 @@ import { AnnotationWrapper } from './annotation.wrapper';
|
||||
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
|
||||
import * as moment from 'moment';
|
||||
|
||||
export class AnnotationData {
|
||||
visibleAnnotations: AnnotationWrapper[];
|
||||
allAnnotations: AnnotationWrapper[];
|
||||
}
|
||||
|
||||
export class FileDataModel {
|
||||
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
|
||||
hasChangeLog: boolean;
|
||||
allAnnotations: AnnotationWrapper[];
|
||||
|
||||
constructor(public file: File, public fileData: Blob, public redactionLog: IRedactionLog, public viewedPages?: IViewedPage[]) {}
|
||||
constructor(
|
||||
public file: File,
|
||||
public fileData: Blob,
|
||||
private _redactionLog: IRedactionLog,
|
||||
public viewedPages?: IViewedPage[],
|
||||
private _dictionaryData?: { [p: string]: Dictionary },
|
||||
private _areDevFeaturesEnabled?: boolean,
|
||||
) {
|
||||
this._buildAllAnnotations();
|
||||
}
|
||||
|
||||
getAnnotations(
|
||||
dictionaryData: { [p: string]: Dictionary },
|
||||
currentUser: User,
|
||||
viewMode: ViewMode,
|
||||
areDevFeaturesEnabled: boolean,
|
||||
): AnnotationData {
|
||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||
let allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => ann.manual || !this.file.excludedPages.includes(ann.pageNumber));
|
||||
set redactionLog(redactionLog: IRedactionLog) {
|
||||
this._redactionLog = redactionLog;
|
||||
this._buildAllAnnotations();
|
||||
}
|
||||
|
||||
if (!areDevFeaturesEnabled) {
|
||||
allAnnotations = allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
}
|
||||
get redactionLog() {
|
||||
return this._redactionLog;
|
||||
}
|
||||
|
||||
const visibleAnnotations = allAnnotations.filter(annotation => {
|
||||
getVisibleAnnotations(viewMode: ViewMode) {
|
||||
return this.allAnnotations.filter(annotation => {
|
||||
if (viewMode === 'STANDARD') {
|
||||
return !annotation.isChangeLogRemoved;
|
||||
} else if (viewMode === 'DELTA') {
|
||||
@ -39,11 +39,30 @@ export class FileDataModel {
|
||||
return annotation.isRedacted;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
visibleAnnotations: visibleAnnotations,
|
||||
allAnnotations: allAnnotations,
|
||||
};
|
||||
private _buildAllAnnotations() {
|
||||
const entries: RedactionLogEntryWrapper[] = this._convertData();
|
||||
|
||||
const previousAnnotations = this.allAnnotations || [];
|
||||
this.allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => ann.manual || !this.file.excludedPages.includes(ann.pageNumber));
|
||||
|
||||
if (!this._areDevFeaturesEnabled) {
|
||||
this.allAnnotations = this.allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
}
|
||||
|
||||
this._setHiddenPropertyToNewAnnotations(this.allAnnotations, previousAnnotations);
|
||||
}
|
||||
|
||||
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||
newAnnotations.forEach(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
|
||||
if (oldAnnotation) {
|
||||
newAnnotation.hidden = oldAnnotation.hidden;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _convertData(): RedactionLogEntryWrapper[] {
|
||||
@ -55,6 +74,7 @@ export class FileDataModel {
|
||||
const redactionLogEntryWrapper: RedactionLogEntryWrapper = {};
|
||||
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
|
||||
redactionLogEntryWrapper.type = redactionLogEntry.type;
|
||||
redactionLogEntryWrapper.hintDictionary = this._dictionaryData[redactionLogEntry.type].hint;
|
||||
|
||||
this._isChangeLogEntry(redactionLogEntry, redactionLogEntryWrapper);
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ export interface RedactionLogEntryWrapper {
|
||||
startOffset?: number;
|
||||
type?: string;
|
||||
rectangle?: boolean;
|
||||
hintDictionary?: boolean;
|
||||
|
||||
color?: Array<number>;
|
||||
dictionaryEntry?: boolean;
|
||||
|
||||
@ -13,7 +13,3 @@
|
||||
left: 270px;
|
||||
}
|
||||
}
|
||||
|
||||
.mt-44 {
|
||||
margin-top: 44px;
|
||||
}
|
||||
|
||||
@ -10,10 +10,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.mb-14 {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.technical-name {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@ -60,7 +60,6 @@
|
||||
*ngIf="form.get('userId').value !== ALL_USERS"
|
||||
[user]="form.get('userId').value"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="form.get('userId').value === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-select-trigger>
|
||||
@ -69,7 +68,6 @@
|
||||
*ngIf="userId !== ALL_USERS"
|
||||
[user]="userId"
|
||||
[withName]="true"
|
||||
size="small"
|
||||
></redaction-initials-avatar>
|
||||
<div *ngIf="userId === ALL_USERS" [translate]="ALL_USERS"></div>
|
||||
</mat-option>
|
||||
@ -109,7 +107,7 @@
|
||||
</div>
|
||||
|
||||
<div class="user-column cell">
|
||||
<redaction-initials-avatar [user]="log.userId" [withName]="true" size="small"></redaction-initials-avatar>
|
||||
<redaction-initials-avatar [user]="log.userId" [withName]="true"></redaction-initials-avatar>
|
||||
</div>
|
||||
|
||||
<div [translate]="translations[log.category]" class="cell"></div>
|
||||
|
||||
@ -17,11 +17,3 @@ form {
|
||||
font-size: 16px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mr-0 {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.mr-20 {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
@ -84,7 +84,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mb-5 {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@ -11,4 +11,5 @@ export const defaultColorsTranslations: { [key in DefaultColorType]: string } =
|
||||
requestAdd: _('default-colors-screen.types.requestAdd'),
|
||||
requestRemove: _('default-colors-screen.types.requestRemove'),
|
||||
updatedColor: _('default-colors-screen.types.updatedColor'),
|
||||
ignoredHintColor: _('default-colors-screen.types.ignoredHintColor'),
|
||||
};
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="hardDelete()"
|
||||
*ngIf="listingService.areSomeSelected$ | async"
|
||||
*ngIf="canDeleteSelected$ | async"
|
||||
[tooltip]="'edit-dossier-dialog.deleted-documents.bulk.delete' | translate"
|
||||
[type]="circleButtonTypes.dark"
|
||||
icon="iqser:trash"
|
||||
@ -48,6 +48,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cell user-column">
|
||||
<redaction-initials-avatar [user]="file.assignee" [withName]="true"></redaction-initials-avatar>
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
<iqser-status-bar
|
||||
[configs]="[
|
||||
{
|
||||
color: file.workflowStatus,
|
||||
label: fileStatusTranslations[file.workflowStatus] | translate,
|
||||
length: 1,
|
||||
cssClass: 'all-caps-label'
|
||||
}
|
||||
]"
|
||||
[small]="true"
|
||||
></iqser-status-bar>
|
||||
</div>
|
||||
|
||||
<div class="cell">
|
||||
<span class="small-label">{{ file.softDeleted | date: 'exactDate' }}</span>
|
||||
</div>
|
||||
@ -65,6 +83,7 @@
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="hardDelete([file])"
|
||||
*ngIf="file.canHardDelete"
|
||||
[tooltip]="'edit-dossier-dialog.deleted-documents.action.delete' | translate"
|
||||
[type]="circleButtonTypes.dark"
|
||||
icon="iqser:trash"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Component, forwardRef, Injector, Input, OnInit } from '@angular/core';
|
||||
import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-dossier-section.interface';
|
||||
import { Dossier, IFile } from '@red/domain';
|
||||
import { Dossier, File, IFile } from '@red/domain';
|
||||
import {
|
||||
CircleButtonTypes,
|
||||
ConfirmationDialogInput,
|
||||
@ -21,8 +21,12 @@ import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { DossiersDialogService } from '../../../services/dossiers-dialog.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { workflowFileStatusTranslations } from '../../../translations/file-status-translations';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
|
||||
interface FileListItem extends IFile, IListable {
|
||||
readonly canHardDelete: boolean;
|
||||
readonly canRestore: boolean;
|
||||
readonly restoreDate: string;
|
||||
}
|
||||
@ -37,14 +41,18 @@ interface FileListItem extends IFile, IListable {
|
||||
],
|
||||
})
|
||||
export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileListItem> implements EditDossierSectionInterface, OnInit {
|
||||
readonly fileStatusTranslations = workflowFileStatusTranslations;
|
||||
@Input() dossier: Dossier;
|
||||
readonly changed = false;
|
||||
readonly valid = false;
|
||||
readonly canRestoreSelected$ = this._canRestoreSelected$;
|
||||
readonly canDeleteSelected$ = this._canDeleteSelected$;
|
||||
disabled: boolean;
|
||||
readonly tableColumnConfigs: TableColumnConfig<FileListItem>[] = [
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.name'), width: '3fr' },
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.pages') },
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.assignee'), class: 'user-column' },
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.status') },
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.deleted-on'), sortByKey: 'softDeleted', width: '2fr' },
|
||||
{ label: _('edit-dossier-dialog.deleted-documents.table-col-names.time-to-restore'), sortByKey: 'softDeleted', width: '2fr' },
|
||||
];
|
||||
@ -59,6 +67,8 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _userService: UserService,
|
||||
) {
|
||||
super(_injector);
|
||||
}
|
||||
@ -70,6 +80,13 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
);
|
||||
}
|
||||
|
||||
private get _canDeleteSelected$(): Observable<boolean> {
|
||||
return this.listingService.selectedEntities$.pipe(
|
||||
map(entities => entities.length && !entities.find(file => !file.canHardDelete)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
}
|
||||
|
||||
hardDelete(files = this.listingService.selected) {
|
||||
const data = new ConfirmationDialogInput({
|
||||
title: _('confirmation-dialog.permanently-delete-file.title'),
|
||||
@ -133,21 +150,26 @@ export class EditDossierDeletedDocumentsComponent extends ListingComponent<FileL
|
||||
return files.map(file => this._toListItem(file));
|
||||
}
|
||||
|
||||
private _toListItem(file: IFile): FileListItem {
|
||||
const restoreDate = this._getRestoreDate(file.softDeleted);
|
||||
private _toListItem(_file: IFile): FileListItem {
|
||||
const file = new File(_file, this._userService.getNameForId(_file.assignee));
|
||||
const restoreDate = this._getRestoreDate(_file.softDeleted);
|
||||
return {
|
||||
id: file.fileId,
|
||||
...file,
|
||||
restoreDate,
|
||||
searchKey: file.filename,
|
||||
canRestore: this._canRestoreFile(restoreDate),
|
||||
canRestore: this._canRestore(file, restoreDate),
|
||||
canHardDelete: this._canPerformActions(file),
|
||||
};
|
||||
}
|
||||
|
||||
private _canRestoreFile(restoreDate: string): boolean {
|
||||
const { daysLeft, hoursLeft, minutesLeft } = getLeftDateTime(restoreDate);
|
||||
private _canPerformActions(file: File): boolean {
|
||||
return this._userService.currentUser.isManager || this._permissionsService.canDeleteFile(file);
|
||||
}
|
||||
|
||||
return daysLeft >= 0 && hoursLeft >= 0 && minutesLeft >= 0;
|
||||
private _canRestore(file: File, restoreDate: string): boolean {
|
||||
const { daysLeft, hoursLeft, minutesLeft } = getLeftDateTime(restoreDate);
|
||||
return this._canPerformActions(file) && daysLeft + hoursLeft + minutesLeft > 0;
|
||||
}
|
||||
|
||||
private _getRestoreDate(softDeletedTime: string): string {
|
||||
|
||||
@ -5,6 +5,7 @@ import { EditDossierSaveResult, EditDossierSectionInterface } from '../edit-doss
|
||||
import { downloadTypesTranslations } from '../../../../../translations/download-types-translations';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { ReportTemplateService } from '@services/report-template.service';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-edit-dossier-download-package',
|
||||
@ -25,6 +26,7 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _reportTemplateController: ReportTemplateService,
|
||||
private readonly _formBuilder: FormBuilder,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
get reportTypesLength() {
|
||||
@ -71,6 +73,9 @@ export class EditDossierDownloadPackageComponent implements OnInit, EditDossierS
|
||||
(await this._reportTemplateController.getAvailableReportTemplates(this.dossier.dossierTemplateId).toPromise()) || [];
|
||||
|
||||
this.form = this._getForm();
|
||||
if (!this._permissionsService.canEditDossier()) {
|
||||
this.form.disable();
|
||||
}
|
||||
}
|
||||
|
||||
async save(): EditDossierSaveResult {
|
||||
|
||||
@ -15,6 +15,7 @@ import { Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { EditDossierTeamComponent } from './edit-dossier-team/edit-dossier-team.component';
|
||||
import { BaseDialogComponent } from '@shared/dialog/base-dialog.component';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
|
||||
type Section = 'dossierInfo' | 'downloadPackage' | 'dossierDictionary' | 'members' | 'dossierAttributes' | 'deletedDocuments';
|
||||
|
||||
@ -41,6 +42,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent {
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _changeRef: ChangeDetectorRef,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<EditDossierDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
@ -111,7 +113,7 @@ export class EditDossierDialogComponent extends BaseDialogComponent {
|
||||
}
|
||||
|
||||
get showActionButtons(): boolean {
|
||||
return !['deletedDocuments'].includes(this.activeNav);
|
||||
return !['deletedDocuments'].includes(this.activeNav) && this._permissionsService.canEditDossier();
|
||||
}
|
||||
|
||||
get changed(): boolean {
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
<redaction-team-members
|
||||
(remove)="toggleSelected($event)"
|
||||
[canAdd]="false"
|
||||
[canRemove]="true"
|
||||
[canRemove]="!disabled"
|
||||
[dossierId]="dossier.dossierId"
|
||||
[largeSpacing]="true"
|
||||
[memberIds]="selectedApproversList"
|
||||
@ -21,13 +21,11 @@
|
||||
[unremovableMembers]="[selectedOwnerId]"
|
||||
></redaction-team-members>
|
||||
|
||||
<pre *ngIf="selectedApproversList.length === 0" [innerHTML]="'assign-dossier-owner.dialog.no-approvers' | translate" class="info"></pre>
|
||||
|
||||
<div class="all-caps-label mt-16" translate="assign-dossier-owner.dialog.reviewers"></div>
|
||||
<redaction-team-members
|
||||
(remove)="toggleSelected($event)"
|
||||
[canAdd]="false"
|
||||
[canRemove]="true"
|
||||
[canRemove]="!disabled"
|
||||
[dossierId]="dossier.dossierId"
|
||||
[largeSpacing]="true"
|
||||
[memberIds]="selectedReviewers$ | async"
|
||||
@ -35,34 +33,35 @@
|
||||
[unremovableMembers]="[selectedOwnerId]"
|
||||
></redaction-team-members>
|
||||
|
||||
<pre
|
||||
*ngIf="(selectedReviewers$ | async).length === 0"
|
||||
[innerHTML]="'assign-dossier-owner.dialog.no-reviewers' | translate"
|
||||
class="info"
|
||||
></pre>
|
||||
<ng-container *ngIf="(selectedReviewers$ | async).length === 0">
|
||||
<div class="info mt-4">{{ 'assign-dossier-owner.dialog.no-reviewers' | translate }}</div>
|
||||
<div *ngIf="!disabled" class="info">{{ 'assign-dossier-owner.dialog.select-below' | translate }}</div>
|
||||
</ng-container>
|
||||
|
||||
<iqser-input-with-action
|
||||
(valueChange)="setMembersSelectOptions($event)"
|
||||
[(value)]="searchQuery"
|
||||
[placeholder]="'assign-dossier-owner.dialog.search' | translate"
|
||||
[width]="560"
|
||||
class="search-container"
|
||||
></iqser-input-with-action>
|
||||
<ng-container *ngIf="!disabled">
|
||||
<iqser-input-with-action
|
||||
(valueChange)="setMembersSelectOptions($event)"
|
||||
[(value)]="searchQuery"
|
||||
[placeholder]="'assign-dossier-owner.dialog.search' | translate"
|
||||
[width]="560"
|
||||
class="search-container"
|
||||
></iqser-input-with-action>
|
||||
|
||||
<div class="members-list">
|
||||
<div
|
||||
(click)="!isOwner(userId) && toggleSelected(userId)"
|
||||
*ngFor="let userId of membersSelectOptions"
|
||||
[class.selected]="isMemberSelected(userId)"
|
||||
>
|
||||
<redaction-initials-avatar [user]="userId" [withName]="true" size="large"></redaction-initials-avatar>
|
||||
<div class="actions">
|
||||
<div (click)="toggleApprover(userId, $event)" *ngIf="!isOwner(userId)" class="make-approver">
|
||||
<iqser-round-checkbox [active]="isApprover(userId)" class="mr-8"></iqser-round-checkbox>
|
||||
<span translate="assign-dossier-owner.dialog.make-approver"></span>
|
||||
<div class="members-list">
|
||||
<div
|
||||
(click)="!isOwner(userId) && toggleSelected(userId)"
|
||||
*ngFor="let userId of membersSelectOptions"
|
||||
[class.selected]="isMemberSelected(userId)"
|
||||
>
|
||||
<redaction-initials-avatar [user]="userId" [withName]="true" size="large"></redaction-initials-avatar>
|
||||
<div class="actions">
|
||||
<div (click)="toggleApprover(userId, $event)" *ngIf="!isOwner(userId)" class="make-approver">
|
||||
<iqser-round-checkbox [active]="isApprover(userId)" class="mr-8"></iqser-round-checkbox>
|
||||
<span translate="assign-dossier-owner.dialog.make-approver"></span>
|
||||
</div>
|
||||
<mat-icon *ngIf="!isOwner(userId)" svgIcon="iqser:check"></mat-icon>
|
||||
</div>
|
||||
<mat-icon *ngIf="!isOwner(userId)" svgIcon="iqser:check"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</form>
|
||||
|
||||
@ -60,7 +60,3 @@ redaction-team-members {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { Dossier, IDossierRequest } from '@red/domain';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
@ -146,7 +146,10 @@ export class EditDossierTeamComponent extends AutoUnsubscribe implements EditDos
|
||||
|
||||
private _loadData() {
|
||||
this.form = this._formBuilder.group({
|
||||
owner: [this.dossier?.ownerId, Validators.required],
|
||||
owner: {
|
||||
value: this.dossier?.ownerId,
|
||||
disabled: this.disabled,
|
||||
},
|
||||
approvers: [[...this.dossier?.approverIds]],
|
||||
members: [[...this.dossier?.memberIds]],
|
||||
});
|
||||
|
||||
@ -50,7 +50,13 @@
|
||||
</div>
|
||||
|
||||
<div class="due-date">
|
||||
<mat-checkbox (change)="hasDueDate = !hasDueDate" [checked]="hasDueDate" class="filter-menu-checkbox" color="primary">
|
||||
<mat-checkbox
|
||||
(change)="hasDueDate = !hasDueDate"
|
||||
[checked]="hasDueDate"
|
||||
[disabled]="!permissionsService.canEditDossier()"
|
||||
class="filter-menu-checkbox"
|
||||
color="primary"
|
||||
>
|
||||
{{ 'edit-dossier-dialog.general-info.form.due-date' | translate }}
|
||||
</mat-checkbox>
|
||||
|
||||
|
||||
@ -68,6 +68,9 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti
|
||||
ngOnInit() {
|
||||
this._filterInvalidDossierTemplates();
|
||||
this.form = this._getForm();
|
||||
if (!this.permissionsService.canEditDossier()) {
|
||||
this.form.disable();
|
||||
}
|
||||
this.hasDueDate = !!this.dossier.dueDate;
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
<section class="dialog">
|
||||
<form (submit)="handleForceRedaction()" [formGroup]="redactionForm">
|
||||
<div class="dialog-header heading-l" translate="manual-annotation.dialog.header.force"></div>
|
||||
<form (submit)="handleForceAnnotation()" [formGroup]="redactionForm">
|
||||
<div class="dialog-header heading-l" translate="manual-annotation.dialog.header.force-redaction" *ngIf="!isHintDialog"></div>
|
||||
<div class="dialog-header heading-l" translate="manual-annotation.dialog.header.force-hint" *ngIf="isHintDialog"></div>
|
||||
|
||||
<div class="dialog-content">
|
||||
<div class="iqser-input-group required w-400">
|
||||
<div class="iqser-input-group required w-400" *ngIf="!isHintDialog">
|
||||
<label translate="manual-annotation.dialog.content.reason"></label>
|
||||
<mat-select
|
||||
[placeholder]="'manual-annotation.dialog.content.reason-placeholder' | translate"
|
||||
@ -16,14 +17,14 @@
|
||||
</mat-select>
|
||||
</div>
|
||||
|
||||
<div class="iqser-input-group w-400">
|
||||
<div class="iqser-input-group w-400" *ngIf="!isHintDialog">
|
||||
<label translate="manual-annotation.dialog.content.legalBasis"></label>
|
||||
<input [value]="redactionForm.get('reason').value?.legalBasis" disabled type="text" />
|
||||
</div>
|
||||
|
||||
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
|
||||
<label translate="manual-annotation.dialog.content.comment"></label>
|
||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
|
||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -17,11 +17,11 @@ export interface LegalBasisOption {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-force-redaction-dialog',
|
||||
templateUrl: './force-redaction-dialog.component.html',
|
||||
styleUrls: ['./force-redaction-dialog.component.scss'],
|
||||
selector: 'redaction-force-annotation-dialog',
|
||||
templateUrl: './force-annotation-dialog.component.html',
|
||||
styleUrls: ['./force-annotation-dialog.component.scss'],
|
||||
})
|
||||
export class ForceRedactionDialogComponent implements OnInit {
|
||||
export class ForceAnnotationDialogComponent implements OnInit {
|
||||
redactionForm: FormGroup;
|
||||
isDocumentAdmin: boolean;
|
||||
legalOptions: LegalBasisOption[] = [];
|
||||
@ -35,17 +35,21 @@ export class ForceRedactionDialogComponent implements OnInit {
|
||||
private readonly _justificationsService: JustificationsService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
public dialogRef: MatDialogRef<ForceRedactionDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) private readonly _data: { readonly dossier: Dossier },
|
||||
public dialogRef: MatDialogRef<ForceAnnotationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) private readonly _data: { readonly dossier: Dossier; readonly hint: boolean },
|
||||
) {
|
||||
this.redactionForm = this._getForm();
|
||||
}
|
||||
|
||||
get isHintDialog() {
|
||||
return this._data.hint;
|
||||
}
|
||||
|
||||
private _getForm(): FormGroup {
|
||||
this.isDocumentAdmin = this._permissionsService.isApprover(this._data.dossier);
|
||||
|
||||
return this._formBuilder.group({
|
||||
reason: [null, Validators.required],
|
||||
reason: this._data.hint ? ['Forced Hint'] : [null, Validators.required],
|
||||
comment: this.isDocumentAdmin ? [null] : [null, Validators.required],
|
||||
});
|
||||
}
|
||||
@ -62,7 +66,7 @@ export class ForceRedactionDialogComponent implements OnInit {
|
||||
this.legalOptions.sort((a, b) => a.label.localeCompare(b.label));
|
||||
}
|
||||
|
||||
handleForceRedaction() {
|
||||
handleForceAnnotation() {
|
||||
this.dialogRef.close(this._createForceRedactionRequest());
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
(data.removeFromDictionary
|
||||
? 'remove-annotations-dialog.remove-from-dictionary.title'
|
||||
: 'remove-annotations-dialog.remove-only-here.title'
|
||||
) | translate
|
||||
) | translate: { hint: data.hint }
|
||||
}}
|
||||
</div>
|
||||
<form (submit)="confirm()" [formGroup]="redactionForm">
|
||||
@ -13,7 +13,7 @@
|
||||
(data.removeFromDictionary
|
||||
? 'remove-annotations-dialog.remove-from-dictionary.question'
|
||||
: 'remove-annotations-dialog.remove-only-here.question'
|
||||
) | translate
|
||||
) | translate: { hint: data.hint }
|
||||
}}
|
||||
|
||||
<div *ngIf="data.removeFromDictionary" class="content-wrapper">
|
||||
|
||||
@ -10,6 +10,7 @@ import { Dossier } from '@red/domain';
|
||||
export interface RemoveAnnotationsDialogInput {
|
||||
annotationsToRemove: AnnotationWrapper[];
|
||||
removeFromDictionary: boolean;
|
||||
hint: boolean;
|
||||
dossier: Dossier;
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
|
||||
import { AddDossierDialogComponent } from './dialogs/add-dossier-dialog/add-dossier-dialog.component';
|
||||
import { AssignReviewerApproverDialogComponent } from './dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component';
|
||||
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { ForceRedactionDialogComponent } from './dialogs/force-redaction-dialog/force-redaction-dialog.component';
|
||||
import { ForceAnnotationDialogComponent } from './dialogs/force-redaction-dialog/force-annotation-dialog.component';
|
||||
import { RemoveAnnotationsDialogComponent } from './dialogs/remove-annotations-dialog/remove-annotations-dialog.component';
|
||||
import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
@ -35,7 +35,7 @@ const dialogs = [
|
||||
AddDossierDialogComponent,
|
||||
EditDossierDialogComponent,
|
||||
ManualAnnotationDialogComponent,
|
||||
ForceRedactionDialogComponent,
|
||||
ForceAnnotationDialogComponent,
|
||||
RemoveAnnotationsDialogComponent,
|
||||
ResizeAnnotationDialogComponent,
|
||||
DocumentInfoDialogComponent,
|
||||
|
||||
@ -54,7 +54,3 @@ redaction-file-workload {
|
||||
redaction-file-actions:not(.keep-visible) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@
|
||||
>
|
||||
<iqser-circle-button
|
||||
(action)="openEditDossierDialog($event, dossier.dossierId)"
|
||||
*ngIf="currentUser.isManager"
|
||||
[tooltip]="'dossier-listing.edit.action' | translate"
|
||||
*ngIf="currentUser.isUser"
|
||||
[tooltip]="(currentUser.isManager ? 'dossier-listing.edit.action' : 'dossier-listing.dossier-info.action') | translate"
|
||||
[type]="circleButtonTypes.dark"
|
||||
icon="iqser:edit"
|
||||
[icon]="currentUser.isManager ? 'iqser:edit' : 'iqser:dossier-info'"
|
||||
iqserHelpMode="edit-dossier-from-list"
|
||||
></iqser-circle-button>
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceRedaction($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, file, annotationsChanged, false)"
|
||||
*ngIf="annotationPermissions.canForceRedaction"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-redaction.label' | translate"
|
||||
@ -96,6 +96,15 @@
|
||||
icon="red:thumb-up"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, file, annotationsChanged, true)"
|
||||
*ngIf="annotationPermissions.canForceHint"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-hint.label' | translate"
|
||||
[type]="buttonType"
|
||||
icon="red:thumb-up"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="hideAnnotation($event)"
|
||||
*ngIf="isImage && isVisible"
|
||||
@ -115,7 +124,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, true)"
|
||||
(action)="removeOrSuggestRemoveAnnotation($event, true)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveFromDictionary"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.remove-from-dict' | translate"
|
||||
@ -135,7 +144,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="suggestRemoveAnnotations($event, false)"
|
||||
(action)="removeOrSuggestRemoveAnnotation($event, false)"
|
||||
*ngIf="annotationPermissions.canRemoveOrSuggestToRemoveOnlyHere"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.remove-annotation.only-here' | translate"
|
||||
|
||||
@ -77,9 +77,15 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
this._setPermissions();
|
||||
}
|
||||
|
||||
suggestRemoveAnnotations($event, removeFromDict: boolean) {
|
||||
removeOrSuggestRemoveAnnotation($event, removeFromDict: boolean) {
|
||||
$event.stopPropagation();
|
||||
this.annotationActionsService.suggestRemoveAnnotation($event, this.annotations, this.file, removeFromDict, this.annotationsChanged);
|
||||
this.annotationActionsService.removeOrSuggestRemoveAnnotation(
|
||||
$event,
|
||||
this.annotations,
|
||||
this.file,
|
||||
removeFromDict,
|
||||
this.annotationsChanged,
|
||||
);
|
||||
}
|
||||
|
||||
markAsFalsePositive($event) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { ConfigService } from '@services/config.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
@ -31,6 +31,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _permissionService: PermissionsService,
|
||||
) {
|
||||
super();
|
||||
@ -41,12 +42,16 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
|
||||
}
|
||||
|
||||
private _setReadState() {
|
||||
const readBefore = this.read;
|
||||
const activePage = this.activePage;
|
||||
if (!activePage) {
|
||||
this.read = false;
|
||||
} else {
|
||||
// console.log('setting read to',activePage.showAsUnseen, !activePage.showAsUnseen);
|
||||
this.read = !activePage.showAsUnseen;
|
||||
}
|
||||
// console.log(this.number, readBefore, activePage, this.read);
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
|
||||
@ -583,7 +583,7 @@ export class PdfViewerComponent implements OnInit, OnChanges {
|
||||
try {
|
||||
this.instance.UI.enableTools(['AnnotationCreateRectangle']);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// happens
|
||||
}
|
||||
this.instance.UI.enableElements(elements);
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ export class TypeAnnotationIconComponent implements OnChanges {
|
||||
this.type =
|
||||
this.annotation.isSuggestion || this.annotation.isDeclinedSuggestion
|
||||
? 'rhombus'
|
||||
: this.annotation.isHint
|
||||
: this.annotation.isHint || this.annotation.isIgnoredHint
|
||||
? 'circle'
|
||||
: this.annotation.isRecommendation
|
||||
? 'hexagon'
|
||||
|
||||
@ -26,12 +26,7 @@
|
||||
|
||||
<div class="vertical-line"></div>
|
||||
|
||||
<redaction-file-actions
|
||||
#fileActions
|
||||
(ocredFile)="ocredFile()"
|
||||
[file]="file"
|
||||
type="file-preview"
|
||||
></redaction-file-actions>
|
||||
<redaction-file-actions [file]="file" type="file-preview"></redaction-file-actions>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="toggleFullScreen()"
|
||||
@ -77,7 +72,7 @@
|
||||
(pageChanged)="viewerPageChanged($event)"
|
||||
(viewerReady)="viewerReady($event)"
|
||||
*ngIf="displayPdfViewer"
|
||||
[annotations]="annotations"
|
||||
[annotations]="visibleAnnotations"
|
||||
[canPerformActions]="canPerformAnnotationActions$ | async"
|
||||
[class.hidden]="!ready"
|
||||
[dossier]="dossier"
|
||||
@ -112,7 +107,7 @@
|
||||
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="annotations"
|
||||
[annotations]="visibleAnnotations"
|
||||
[dialogRef]="dialogRef"
|
||||
[file]="file"
|
||||
[hideSkipped]="hideSkipped"
|
||||
@ -143,7 +138,7 @@
|
||||
<ng-container *ngIf="!filter.topLevelFilter">
|
||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.id" [dossierTemplateId]="dossier.dossierTemplateId">
|
||||
</redaction-dictionary-annotation-icon>
|
||||
{{ filter.id | humanize: false }}
|
||||
{{ filter.label | humanize: false }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
@ -53,10 +53,6 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ml-2 {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.analysis-progress {
|
||||
padding: 12px 20px;
|
||||
max-width: 400px;
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ChangeDetectorRef, Component, HostListener, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { Core, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { PdfViewerComponent } from './components/pdf-viewer/pdf-viewer.component';
|
||||
import {
|
||||
@ -19,14 +18,13 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
|
||||
import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { AnnotationDrawService } from '../../services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { Dossier, File, ViewMode } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, Observable, timer } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
|
||||
import { download } from '@utils/file-download-utils';
|
||||
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
||||
@ -70,7 +68,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
fullScreen = false;
|
||||
shouldDeselectAnnotationsOnPageChange = true;
|
||||
fileData: FileDataModel;
|
||||
annotationData: AnnotationData;
|
||||
selectedAnnotations: AnnotationWrapper[] = [];
|
||||
hideSkipped = false;
|
||||
displayPdfViewer = false;
|
||||
@ -84,7 +81,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
ready = false;
|
||||
private _instance: WebViewerInstance;
|
||||
private _lastPage: string;
|
||||
private _reloadFileOnReanalysis = false;
|
||||
@ViewChild('fileWorkloadComponent') private readonly _workloadComponent: FileWorkloadComponent;
|
||||
@ViewChild('annotationFilterTemplate', {
|
||||
read: TemplateRef,
|
||||
@ -95,8 +91,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userService: UserService,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
@ -138,8 +132,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
});
|
||||
}
|
||||
|
||||
get annotations(): AnnotationWrapper[] {
|
||||
return this.annotationData ? this.annotationData.visibleAnnotations : [];
|
||||
get visibleAnnotations(): AnnotationWrapper[] {
|
||||
return this.fileData ? this.fileData.getVisibleAnnotations(this.viewModeService.viewMode) : [];
|
||||
}
|
||||
|
||||
get allAnnotations(): AnnotationWrapper[] {
|
||||
return this.fileData ? this.fileData.allAnnotations : [];
|
||||
}
|
||||
|
||||
get activeViewer(): WebViewerInstance {
|
||||
@ -167,7 +165,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
async updateViewMode(): Promise<void> {
|
||||
const ocrAnnotationIds = this.annotationData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
|
||||
const ocrAnnotationIds = this.fileData.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'));
|
||||
|
||||
@ -249,19 +247,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
console.log(`[REDACTION] Delete previous annotations time: ${new Date().getTime() - startTime} ms`);
|
||||
}
|
||||
const processStartTime = new Date().getTime();
|
||||
const dossier = this._dossiersService.find(this.dossierId);
|
||||
const newAnnotationsData = this.fileData.getAnnotations(
|
||||
this._appStateService.dictionaryData[dossier.dossierTemplateId],
|
||||
this._userService.currentUser,
|
||||
this.viewModeService.viewMode,
|
||||
this.userPreferenceService.areDevFeaturesEnabled,
|
||||
);
|
||||
if (this.annotationData) {
|
||||
this._setHiddenPropertyToNewAnnotations(newAnnotationsData.visibleAnnotations, this.annotationData.visibleAnnotations);
|
||||
this._setHiddenPropertyToNewAnnotations(newAnnotationsData.allAnnotations, this.annotationData.allAnnotations);
|
||||
}
|
||||
this.annotationData = newAnnotationsData;
|
||||
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
|
||||
|
||||
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.visibleAnnotations);
|
||||
const primaryFilters = this._filterService.getGroup('primaryFilters')?.filters;
|
||||
this._filterService.addFilterGroup({
|
||||
slug: 'primaryFilters',
|
||||
@ -282,7 +269,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
handleAnnotationSelected(annotationIds: string[]) {
|
||||
// TODO: use includes() here
|
||||
this.selectedAnnotations = annotationIds
|
||||
.map(id => this.annotations.find(annotationWrapper => annotationWrapper.id === id))
|
||||
.map(id => this.visibleAnnotations.find(annotationWrapper => annotationWrapper.id === id))
|
||||
.filter(ann => ann !== undefined);
|
||||
if (this.selectedAnnotations.length > 1) {
|
||||
this.multiSelectService.activate();
|
||||
@ -429,10 +416,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
await this._reloadAnnotationsForPage(annotation?.pageNumber || this.activeViewerPage);
|
||||
}
|
||||
|
||||
ocredFile(): void {
|
||||
this._reloadFileOnReanalysis = true;
|
||||
}
|
||||
|
||||
closeFullScreen() {
|
||||
if (!!document.fullscreenElement && document.exitFullscreen) {
|
||||
document.exitFullscreen().then();
|
||||
@ -478,19 +461,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private async _reloadFile(file: File): Promise<void> {
|
||||
await this._loadFileData(file, true);
|
||||
await this._loadFileData(file);
|
||||
await this._stampPDF();
|
||||
}
|
||||
|
||||
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||
newAnnotations.forEach(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
|
||||
if (oldAnnotation) {
|
||||
newAnnotation.hidden = oldAnnotation.hidden;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async _stampPDF() {
|
||||
if (!this._instance) {
|
||||
return;
|
||||
@ -552,36 +526,26 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this.addSubscription = this._filesMapService.fileReanalysed$
|
||||
.pipe(filter(file => file.fileId === this.fileId))
|
||||
.subscribe(async file => {
|
||||
await this._loadFileData(file, !this._reloadFileOnReanalysis);
|
||||
this._reloadFileOnReanalysis = false;
|
||||
await this._reloadAnnotations();
|
||||
if (file.lastProcessed !== this.fileData?.file.lastProcessed) {
|
||||
await this._loadFileData(file);
|
||||
await this._reloadAnnotations();
|
||||
}
|
||||
this._loadingService.stop();
|
||||
});
|
||||
}
|
||||
|
||||
private async _loadFileData(file: File, performUpdate = false): Promise<void | boolean> {
|
||||
private async _loadFileData(file: File): Promise<void | boolean> {
|
||||
if (!file || file.isError) {
|
||||
return this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]);
|
||||
}
|
||||
|
||||
const fileData = await this._fileDownloadService.loadDataFor(file).toPromise();
|
||||
const fileData = await this._fileDownloadService.loadDataFor(file, this.fileData).toPromise();
|
||||
|
||||
if (file.isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (performUpdate && !!this.fileData) {
|
||||
this.fileData.redactionLog = fileData.redactionLog;
|
||||
this.fileData.viewedPages = fileData.viewedPages;
|
||||
const excludedOrIncludedPages = new Set(diff(this.fileData.file.excludedPages, file.excludedPages));
|
||||
const currentPageAnnotations = this.annotations.filter(a => excludedOrIncludedPages.has(a.pageNumber));
|
||||
this.fileData.file = file;
|
||||
if (excludedOrIncludedPages?.size) {
|
||||
await this._cleanupAndRedrawAnnotations(currentPageAnnotations, a => excludedOrIncludedPages.has(a.pageNumber));
|
||||
}
|
||||
} else {
|
||||
this.fileData = fileData;
|
||||
}
|
||||
this.fileData = fileData;
|
||||
}
|
||||
|
||||
@Debounce(0)
|
||||
@ -592,19 +556,30 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
private async _reloadAnnotations() {
|
||||
this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
|
||||
await this._cleanupAndRedrawAnnotations(this.annotations);
|
||||
this._instance.Core.annotationManager.deleteAnnotations(this._instance.Core.annotationManager.getAnnotationsList(), {
|
||||
imported: true,
|
||||
force: true,
|
||||
});
|
||||
await this._cleanupAndRedrawAnnotations();
|
||||
}
|
||||
|
||||
private async _reloadAnnotationsForPage(page: number) {
|
||||
const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page);
|
||||
this.fileData.file = await this._filesService.reload(this.dossierId, this.fileId).toPromise();
|
||||
|
||||
// if this action triggered a re-processing,
|
||||
// we don't want to redraw for this page since they will get redrawn as soon as processing ends;
|
||||
if (this.fileData.file.isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPageAnnotations = this.visibleAnnotations.filter(a => a.pageNumber === page);
|
||||
this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
|
||||
|
||||
await this._cleanupAndRedrawAnnotations(currentPageAnnotations, annotation => annotation.pageNumber === page);
|
||||
}
|
||||
|
||||
private async _cleanupAndRedrawAnnotations(
|
||||
annotationsToDelete: AnnotationWrapper[],
|
||||
annotationsToDelete?: AnnotationWrapper[],
|
||||
newAnnotationsFilter?: (annotation: AnnotationWrapper) => boolean,
|
||||
) {
|
||||
this.rebuildFilters();
|
||||
@ -614,7 +589,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
annotationsToDelete?.forEach(annotation => {
|
||||
this._findAndDeleteAnnotation(annotation.id);
|
||||
});
|
||||
const newAnnotations = newAnnotationsFilter ? this.annotations.filter(newAnnotationsFilter) : this.annotations;
|
||||
const newAnnotations = newAnnotationsFilter ? this.allAnnotations.filter(newAnnotationsFilter) : this.allAnnotations;
|
||||
this._handleDeltaAnnotationFilters(annotationsToDelete ?? [], newAnnotations);
|
||||
await this._redrawAnnotations(newAnnotations);
|
||||
console.log(
|
||||
@ -623,7 +598,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
}
|
||||
|
||||
private _redrawAnnotations(annotations = this.annotationData.allAnnotations) {
|
||||
private _redrawAnnotations(annotations = this.allAnnotations) {
|
||||
return this._annotationDrawService.drawAnnotations(
|
||||
this._instance,
|
||||
annotations,
|
||||
|
||||
@ -59,9 +59,15 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
forceRedaction($event: MouseEvent, annotations: AnnotationWrapper[], file: File, annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
const data = { dossier: this._dossier(file) };
|
||||
this._dialogService.openDialog('forceRedaction', $event, data, (request: ILegalBasisChangeRequest) => {
|
||||
forceAnnotation(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
hint: boolean = false,
|
||||
) {
|
||||
const data = { dossier: this._dossier(file), hint };
|
||||
this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.force(
|
||||
@ -107,14 +113,19 @@ export class AnnotationActionsService {
|
||||
);
|
||||
}
|
||||
|
||||
suggestRemoveAnnotation(
|
||||
removeOrSuggestRemoveAnnotation(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
removeFromDictionary: boolean,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
const data = { annotationsToRemove: annotations, removeFromDictionary, dossier: this._dossier(file) };
|
||||
const data = {
|
||||
annotationsToRemove: annotations,
|
||||
removeFromDictionary,
|
||||
dossier: this._dossier(file),
|
||||
hint: annotations[0].hintDictionary,
|
||||
};
|
||||
this._dialogService.openDialog('removeAnnotations', $event, data, (result: { comment: string }) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
@ -279,7 +290,7 @@ export class AnnotationActionsService {
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.remove-from-dict'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.suggestRemoveAnnotation(null, annotations, file, true, annotationsChanged);
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, file, true, annotationsChanged);
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -349,7 +360,21 @@ export class AnnotationActionsService {
|
||||
title: this._translateService.instant('annotation-actions.force-redaction.label'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.forceRedaction(null, annotations, file, annotationsChanged);
|
||||
this.forceAnnotation(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const canForceHint = annotationPermissions.reduce((acc, next) => acc && next.permissions.canForceHint, true);
|
||||
if (canForceHint) {
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/thumb-up.svg'),
|
||||
title: this._translateService.instant('annotation-actions.force-hint.label'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.forceAnnotation(null, annotations, file, annotationsChanged, true);
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -380,7 +405,7 @@ export class AnnotationActionsService {
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.only-here'),
|
||||
onClick: () => {
|
||||
this._ngZone.run(() => {
|
||||
this.suggestRemoveAnnotation(null, annotations, file, false, annotationsChanged);
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, file, false, annotationsChanged);
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -416,7 +441,7 @@ export class AnnotationActionsService {
|
||||
viewer: WebViewerInstance,
|
||||
file: File,
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
annotationsChanged?: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
const data = { dossier: this._dossier(file) };
|
||||
this._dialogService.openDialog('resizeAnnotation', $event, data, async (result: { comment: string }) => {
|
||||
|
||||
@ -42,27 +42,26 @@ export class AnnotationProcessingService {
|
||||
const filters: INestedFilter[] = [];
|
||||
|
||||
annotations?.forEach(a => {
|
||||
const topLevelFilter = !['hint', 'redaction', 'recommendation', 'skipped'].includes(a.superType);
|
||||
const key = topLevelFilter ? a.superType : a.superType + a.type;
|
||||
const filter = filterMap.get(key);
|
||||
const topLevelFilter = a.topLevelFilter;
|
||||
const filter = filterMap.get(a.filterKey);
|
||||
if (filter) {
|
||||
filter.matches += 1;
|
||||
} else {
|
||||
// top level filter
|
||||
if (topLevelFilter) {
|
||||
this._createParentFilter(key, filterMap, filters);
|
||||
this._createParentFilter(a.superType, filterMap, filters);
|
||||
} else {
|
||||
let parentFilter = filterMap.get(a.superType);
|
||||
if (!parentFilter) {
|
||||
parentFilter = this._createParentFilter(a.superType, filterMap, filters);
|
||||
}
|
||||
const childFilter: IFilter = {
|
||||
id: a.type,
|
||||
id: a.filterKey,
|
||||
label: a.type,
|
||||
checked: false,
|
||||
matches: 1,
|
||||
};
|
||||
filterMap.set(key, childFilter);
|
||||
filterMap.set(a.filterKey, childFilter);
|
||||
parentFilter.children.push(new Filter(childFilter));
|
||||
}
|
||||
}
|
||||
@ -173,12 +172,7 @@ export class AnnotationProcessingService {
|
||||
return true;
|
||||
};
|
||||
|
||||
private _checkByFilterKey = (filter: INestedFilter, annotation: AnnotationWrapper) => {
|
||||
const superType = annotation.superType;
|
||||
const isNotTopLevelFilter = superType === 'hint' || superType === 'redaction' || superType === 'recommendation';
|
||||
|
||||
return filter.id === superType || (filter.id === annotation.type && isNotTopLevelFilter);
|
||||
};
|
||||
private _checkByFilterKey = (filter: NestedFilter | IFilter, annotation: AnnotationWrapper) => filter.id === annotation.filterKey;
|
||||
|
||||
private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
|
||||
return annotations.sort((ann1, ann2) => {
|
||||
|
||||
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AddDossierDialogComponent } from '../dialogs/add-dossier-dialog/add-dossier-dialog.component';
|
||||
import { RemoveAnnotationsDialogComponent } from '../dialogs/remove-annotations-dialog/remove-annotations-dialog.component';
|
||||
import { ForceRedactionDialogComponent } from '../dialogs/force-redaction-dialog/force-redaction-dialog.component';
|
||||
import { ForceAnnotationDialogComponent } from '../dialogs/force-redaction-dialog/force-annotation-dialog.component';
|
||||
import { DocumentInfoDialogComponent } from '../dialogs/document-info-dialog/document-info-dialog.component';
|
||||
import { ManualAnnotationDialogComponent } from '../dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||
import { EditDossierDialogComponent } from '../dialogs/edit-dossier-dialog/edit-dossier-dialog.component';
|
||||
@ -22,7 +22,7 @@ type DialogType =
|
||||
| 'changeLegalBasis'
|
||||
| 'removeAnnotations'
|
||||
| 'resizeAnnotation'
|
||||
| 'forceRedaction'
|
||||
| 'forceAnnotation'
|
||||
| 'manualAnnotation';
|
||||
|
||||
@Injectable()
|
||||
@ -58,8 +58,8 @@ export class DossiersDialogService extends DialogService<DialogType> {
|
||||
resizeAnnotation: {
|
||||
component: ResizeAnnotationDialogComponent,
|
||||
},
|
||||
forceRedaction: {
|
||||
component: ForceRedactionDialogComponent,
|
||||
forceAnnotation: {
|
||||
component: ForceAnnotationDialogComponent,
|
||||
},
|
||||
manualAnnotation: {
|
||||
component: ManualAnnotationDialogComponent,
|
||||
|
||||
@ -7,14 +7,20 @@ import { File } from '@red/domain';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { RedactionLogService } from './redaction-log.service';
|
||||
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
|
||||
import { AppStateService } from '../../../state/app-state.service';
|
||||
import { DossiersService } from '../../../services/entity-services/dossiers.service';
|
||||
import { UserPreferenceService } from '../../../services/user-preference.service';
|
||||
|
||||
@Injectable()
|
||||
export class PdfViewerDataService {
|
||||
constructor(
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _redactionLogService: RedactionLogService,
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
private readonly _appStateService: AppStateService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
) {}
|
||||
|
||||
loadRedactionLogFor(dossierId: string, fileId: string) {
|
||||
@ -24,12 +30,24 @@ export class PdfViewerDataService {
|
||||
);
|
||||
}
|
||||
|
||||
loadDataFor(file: File): Observable<FileDataModel> {
|
||||
const file$ = this.downloadOriginalFile(file);
|
||||
loadDataFor(file: File, fileData?: FileDataModel): Observable<FileDataModel> {
|
||||
const file$ = fileData?.file.cacheIdentifier === file.cacheIdentifier ? of(fileData.fileData) : this.downloadOriginalFile(file);
|
||||
const reactionLog$ = this.loadRedactionLogFor(file.dossierId, file.fileId);
|
||||
const viewedPages$ = this.getViewedPagesFor(file);
|
||||
|
||||
return forkJoin([file$, reactionLog$, viewedPages$]).pipe(map(data => new FileDataModel(file, ...data)));
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
|
||||
return forkJoin([file$, reactionLog$, viewedPages$]).pipe(
|
||||
map(
|
||||
data =>
|
||||
new FileDataModel(
|
||||
file,
|
||||
...data,
|
||||
this._appStateService.dictionaryData[dossier.dossierTemplateId],
|
||||
this._userPreferenceService.areDevFeaturesEnabled,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
getViewedPagesFor(file: File) {
|
||||
|
||||
@ -51,7 +51,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
@Input() file: File;
|
||||
@Input() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
|
||||
@Input() maxWidth: number;
|
||||
@Output() readonly ocredFile = new EventEmitter<void>();
|
||||
|
||||
toggleTooltip?: string;
|
||||
assignTooltip?: string;
|
||||
@ -308,7 +307,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
|
||||
$event.stopPropagation();
|
||||
this._loadingService.start();
|
||||
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
|
||||
this.ocredFile.emit();
|
||||
this._loadingService.stop();
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ export const processingFileStatusTranslations: { [key in ProcessingFileStatus]:
|
||||
DELETED: _('file-status.deleted'),
|
||||
ERROR: _('file-status.error'),
|
||||
FULLREPROCESS: _('file-status.full-reprocess'),
|
||||
IMAGE_ANALYZING: _('file-status.image-analyzing'),
|
||||
INDEXING: _('file-status.indexing'),
|
||||
OCR_PROCESSING: _('file-status.ocr-processing'),
|
||||
PROCESSING: _('file-status.processing'),
|
||||
|
||||
@ -28,5 +28,5 @@
|
||||
</div>
|
||||
|
||||
<ng-template #avatar let-userId="userId">
|
||||
<redaction-initials-avatar [user]="userId" [withName]="true" color="gray" size="small"></redaction-initials-avatar>
|
||||
<redaction-initials-avatar [user]="userId" [withName]="true" color="gray"></redaction-initials-avatar>
|
||||
</ng-template>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<button [class.overlay]="showDot" mat-button>
|
||||
<redaction-initials-avatar [user]="userService.currentUser$ | async" [withName]="true" size="small"></redaction-initials-avatar>
|
||||
<redaction-initials-avatar [user]="userService.currentUser$ | async" [withName]="true"></redaction-initials-avatar>
|
||||
<mat-icon svgIcon="iqser:arrow-down"></mat-icon>
|
||||
</button>
|
||||
<div *ngIf="showDot" class="dot"></div>
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
:host {
|
||||
@extend .user-button;
|
||||
min-width: fit-content;
|
||||
|
||||
button {
|
||||
padding: 0 10px 0 5px;
|
||||
|
||||
@ -50,10 +50,6 @@ form {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mr-32 {
|
||||
margin-right: 32px;
|
||||
}
|
||||
|
||||
.w-450 {
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
|
||||
@ -68,6 +68,7 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
glyphMargin: true,
|
||||
automaticLayout: true,
|
||||
readOnly: !this.canEdit,
|
||||
extraEditorClassName: this.canEdit ? '' : 'disabled',
|
||||
};
|
||||
}
|
||||
|
||||
@ -79,19 +80,12 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
this._diffEditor.getModifiedEditor().onDidChangeModelContent(() => {
|
||||
this.value = this._diffEditor.getModel().modified.getValue();
|
||||
});
|
||||
this._setTheme();
|
||||
}
|
||||
|
||||
onCodeEditorInit(editor: ICodeEditor): void {
|
||||
this.codeEditor = editor;
|
||||
(window as any).monaco.editor.defineTheme('redaction', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.lineHighlightBackground': '#f4f5f7',
|
||||
},
|
||||
});
|
||||
(window as any).monaco.editor.setTheme('redaction');
|
||||
this._setTheme();
|
||||
}
|
||||
|
||||
@Debounce()
|
||||
@ -107,6 +101,34 @@ export class EditorComponent implements OnInit, OnChanges {
|
||||
this._diffEditor?.getModifiedEditor().setValue(this.diffValue);
|
||||
}
|
||||
|
||||
private _defineThemes(): void {
|
||||
(window as any).monaco.editor.defineTheme('redaction', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.lineHighlightBackground': '#f4f5f7',
|
||||
},
|
||||
});
|
||||
(window as any).monaco.editor.defineTheme('redaction-disabled', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
rules: [],
|
||||
colors: {
|
||||
'editor.background': '#f4f5f7',
|
||||
'editor.foreground': '#9398a0',
|
||||
'editor.lineHighlightBackground': '#f4f5f7',
|
||||
'editorLineNumber.foreground': '#9398a0',
|
||||
'editorActiveLineNumber.foreground': '#9398a0',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _setTheme(): void {
|
||||
this._defineThemes();
|
||||
(window as any).monaco.editor.setTheme(this.canEdit ? 'redaction' : 'redaction-disabled');
|
||||
}
|
||||
|
||||
private _handleMarginButtonClick(event: IEditorMouseEvent) {
|
||||
const isMarginButtonClick = event.target.detail.glyphMarginWidth && event.target.detail.glyphMarginWidth !== 0;
|
||||
if (isMarginButtonClick) {
|
||||
|
||||
@ -21,7 +21,3 @@ mat-slide-toggle {
|
||||
color: rgba(var(--iqser-accent-rgb), 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.ml-0 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
>
|
||||
{{ _user | name: { showInitials: true } }}
|
||||
</div>
|
||||
<div *ngIf="withName" [class.disabled]="disabled" class="clamp-2 username">
|
||||
<div *ngIf="withName" [class.disabled]="disabled" class="clamp-1 username">
|
||||
{{ userName }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
<div class="label-header">
|
||||
<div class="all-caps-label">{{ label }}</div>
|
||||
<div class="actions">
|
||||
<div (click)="selectAll($event)" class="all-caps-label primary pointer" translate="actions.all"></div>
|
||||
<div (click)="deselectAll($event)" class="all-caps-label primary pointer" translate="actions.none"></div>
|
||||
<div (click)="selectAll($event)" class="all-caps-label primary pointer" [class.disabled]="disabled" translate="actions.all"></div>
|
||||
<div
|
||||
(click)="deselectAll($event)"
|
||||
class="all-caps-label primary pointer"
|
||||
[class.disabled]="disabled"
|
||||
translate="actions.none"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -69,3 +69,8 @@ mat-chip {
|
||||
.mat-standard-chip:focus::after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
color: variables.$grey-5;
|
||||
}
|
||||
|
||||
@ -24,6 +24,12 @@
|
||||
label="S"
|
||||
type="square"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="filter.id === 'ignored-hint'"
|
||||
[color]="dictionaryColor"
|
||||
label="I"
|
||||
type="circle"
|
||||
></redaction-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
*ngIf="isSuggestion(filter.id)"
|
||||
[color]="dictionaryColor"
|
||||
|
||||
@ -14,7 +14,6 @@ import { FileDropOverlayService } from './services/file-drop-overlay.service';
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedModule, OverlayModule],
|
||||
declarations: [FileDropComponent, UploadStatusOverlayComponent, OverwriteFilesDialogComponent],
|
||||
entryComponents: [FileDropComponent, UploadStatusOverlayComponent],
|
||||
providers: [UploadDownloadDialogService, FileUploadService, FileDownloadService, StatusOverlayService, FileDropOverlayService],
|
||||
exports: [FileDropComponent, UploadStatusOverlayComponent],
|
||||
})
|
||||
|
||||
@ -15,7 +15,7 @@ export class PlatformSearchService extends GenericService<ISearchResponse> {
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
) {
|
||||
super(_injector, 'search');
|
||||
super(_injector, 'search-v2');
|
||||
}
|
||||
|
||||
search({ dossierIds, query }: ISearchInput): Observable<ISearchResponse> {
|
||||
@ -29,7 +29,7 @@ export class PlatformSearchService extends GenericService<ISearchResponse> {
|
||||
const body: ISearchRequest = {
|
||||
dossierIds,
|
||||
queryString: query ?? '',
|
||||
page: 1,
|
||||
page: 0,
|
||||
returnSections: false,
|
||||
pageSize: 300,
|
||||
};
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { GenericService, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { IGeneralConfiguration } from '@red/domain';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class GeneralSettingsService extends GenericService<IGeneralConfiguration> {
|
||||
constructor(protected readonly _injector: Injector, private readonly _userService: UserService) {
|
||||
constructor(protected readonly _injector: Injector) {
|
||||
super(_injector, 'configuration');
|
||||
}
|
||||
|
||||
getGeneralConfigurations() {
|
||||
getGeneralConfigurations(): Observable<IGeneralConfiguration> {
|
||||
return this._getOne(['general']);
|
||||
}
|
||||
|
||||
|
||||
@ -122,6 +122,10 @@ export class PermissionsService {
|
||||
return dossier.ownerId === this._userService.currentUser.id;
|
||||
}
|
||||
|
||||
canEditDossier(user = this._userService.currentUser) {
|
||||
return user.isManager;
|
||||
}
|
||||
|
||||
isAdmin(user = this._userService.currentUser): boolean {
|
||||
return user.isAdmin;
|
||||
}
|
||||
@ -131,7 +135,7 @@ export class PermissionsService {
|
||||
}
|
||||
|
||||
canExcludePages(file: File): boolean {
|
||||
return this.isFileAssignee(file);
|
||||
return this.canPerformAnnotationActions(file);
|
||||
}
|
||||
|
||||
canDeleteComment(comment: IComment, file: File) {
|
||||
|
||||
@ -50,15 +50,17 @@ export class UserService extends EntitiesService<User, IUser> {
|
||||
);
|
||||
}
|
||||
|
||||
async loadCurrentUser() {
|
||||
async loadCurrentUser(): Promise<User> {
|
||||
const token = await this._keycloakService.getToken();
|
||||
const decoded = jwt_decode(token);
|
||||
const userId = (<{ sub: string }>decoded).sub;
|
||||
|
||||
const roles = this._keycloakService.getUserRoles(true).filter(role => role.startsWith('RED_'));
|
||||
this.replace(new User(await this._keycloakService.loadUserProfile(true), roles, userId));
|
||||
const user = new User(await this._keycloakService.loadUserProfile(true), roles, userId);
|
||||
this.replace(user);
|
||||
|
||||
this._currentUser$.next(this.find(userId));
|
||||
return user;
|
||||
}
|
||||
|
||||
getNameForId(userId: string): string | undefined {
|
||||
|
||||
@ -176,6 +176,14 @@ export class AppStateService {
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['ignored-hint'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.ignoredHintColor || FALLBACK_COLOR,
|
||||
type: 'ignored-hint',
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
||||
dictionaryData['manual-redaction'] = new Dictionary(
|
||||
{
|
||||
hexColor: colors.manualRedactionColor || FALLBACK_COLOR,
|
||||
|
||||
@ -4,6 +4,7 @@ import { AnnotationSuperType } from '../models/file/annotation.wrapper';
|
||||
export const annotationTypesTranslations: { [key in AnnotationSuperType]: string } = {
|
||||
'declined-suggestion': _('annotation-type.declined-suggestion'),
|
||||
hint: _('annotation-type.hint'),
|
||||
'ignored-hint': _('annotation-type.ignored-hint'),
|
||||
'manual-redaction': _('annotation-type.manual-redaction'),
|
||||
recommendation: _('annotation-type.recommendation'),
|
||||
redaction: _('annotation-type.redaction'),
|
||||
|
||||
@ -6,6 +6,7 @@ import { KeycloakEventType, KeycloakService } from 'keycloak-angular';
|
||||
import { GeneralSettingsService } from '@services/general-settings.service';
|
||||
import { LanguageService } from '@i18n/language.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
|
||||
export function configurationInitializer(
|
||||
keycloakService: KeycloakService,
|
||||
@ -13,6 +14,7 @@ export function configurationInitializer(
|
||||
configService: ConfigService,
|
||||
generalSettingsService: GeneralSettingsService,
|
||||
languageService: LanguageService,
|
||||
userService: UserService,
|
||||
userPreferenceService: UserPreferenceService,
|
||||
) {
|
||||
return () =>
|
||||
@ -21,14 +23,16 @@ export function configurationInitializer(
|
||||
filter(event => event.type === KeycloakEventType.OnReady),
|
||||
switchMap(() => from(keycloakService.isLoggedIn())),
|
||||
switchMap(loggedIn => (!loggedIn ? throwError('Not Logged In') : of({}))),
|
||||
switchMap(() => from(userService.loadCurrentUser())),
|
||||
switchMap(user => (!user.hasAnyREDRoles ? throwError('Not user has no red roles') : of({}))),
|
||||
mergeMap(() => generalSettingsService.getGeneralConfigurations()),
|
||||
tap(configuration => configService.updateDisplayName(configuration.displayName)),
|
||||
switchMap(() => userPreferenceService.reload()),
|
||||
tap(() => languageService.chooseAndSetInitialLanguage()),
|
||||
catchError(() => {
|
||||
title.setTitle('RedactManager');
|
||||
return of({});
|
||||
}),
|
||||
tap(() => languageService.chooseAndSetInitialLanguage()),
|
||||
take(1),
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
@ -10,6 +10,7 @@ export const SuperTypeSorter: { [key in AnnotationSuperType]: number } = {
|
||||
'suggestion-remove-dictionary': 13,
|
||||
'suggestion-add': 10,
|
||||
'suggestion-remove': 11,
|
||||
'ignored-hint': 45,
|
||||
skipped: 50,
|
||||
redaction: 1,
|
||||
'manual-redaction': 2,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"ADMIN_CONTACT_NAME": null,
|
||||
"ADMIN_CONTACT_URL": null,
|
||||
"API_URL": "https://dev-03.iqser.cloud/redaction-gateway-v1",
|
||||
"API_URL": "https://dev-08.iqser.cloud/redaction-gateway-v1",
|
||||
"APP_NAME": "RedactManager",
|
||||
"AUTO_READ_TIME": 1.5,
|
||||
"BACKEND_APP_VERSION": "4.4.40",
|
||||
@ -17,7 +17,7 @@
|
||||
"MAX_RETRIES_ON_SERVER_ERROR": 3,
|
||||
"OAUTH_CLIENT_ID": "redaction",
|
||||
"OAUTH_IDP_HINT": null,
|
||||
"OAUTH_URL": "https://dev-03.iqser.cloud/auth/realms/redaction",
|
||||
"OAUTH_URL": "https://dev-08.iqser.cloud/auth/realms/redaction",
|
||||
"RECENT_PERIOD_IN_HOURS": 24,
|
||||
"SELECTION_MODE": "structural"
|
||||
}
|
||||
|
||||
@ -473,7 +473,8 @@
|
||||
"previewColor": "Vorschau",
|
||||
"requestAdd": "Neuen Wörterbucheintrag vorschlagen",
|
||||
"requestRemove": "Anfrage entfernt",
|
||||
"updatedColor": "Aktualisiert"
|
||||
"updatedColor": "Aktualisiert",
|
||||
"ignoredHintColor": "Ignorierter Hinweis"
|
||||
}
|
||||
},
|
||||
"dev-mode": "DEV",
|
||||
|
||||
@ -151,6 +151,9 @@
|
||||
"edit-reason": {
|
||||
"label": "Edit Reason"
|
||||
},
|
||||
"force-hint": {
|
||||
"label": "Force Hint"
|
||||
},
|
||||
"force-redaction": {
|
||||
"label": "Force Redaction"
|
||||
},
|
||||
@ -277,6 +280,7 @@
|
||||
"annotation-type": {
|
||||
"declined-suggestion": "Declined Suggestion",
|
||||
"hint": "Hint",
|
||||
"ignored-hint": "Ignored Hint",
|
||||
"manual-redaction": "Manual Redaction",
|
||||
"recommendation": "Recommendation",
|
||||
"redaction": "Redaction",
|
||||
@ -296,10 +300,10 @@
|
||||
"dialog": {
|
||||
"approvers": "Approvers",
|
||||
"make-approver": "Make Approver",
|
||||
"no-approvers": "No approvers yet.\nSelect from the list below.",
|
||||
"no-reviewers": "No reviewers yet.\nSelect from the list below.",
|
||||
"no-reviewers": "No members with \"review only\" permission.",
|
||||
"reviewers": "Reviewers",
|
||||
"search": "Search...",
|
||||
"select-below": "Select from the list below.",
|
||||
"single-user": "Owner"
|
||||
}
|
||||
},
|
||||
@ -351,10 +355,10 @@
|
||||
"to": "to"
|
||||
},
|
||||
"auth-error": {
|
||||
"heading": "Your user doesn't have the required RED-* roles to access this application. Please contact your admin for access!",
|
||||
"heading-with-link": "Your user doesn't have the required RED-* roles to access this application. Please contact <a href={adminUrl} target=_blank >your admin</a> for access!",
|
||||
"heading-with-name": "Your user doesn't have the required RED-* roles to access this application. Please contact {adminName} for access!",
|
||||
"heading-with-name-and-link": "Your user doesn't have the required RED-* roles to access this application. Please contact <a href={adminUrl} target=_blank >{adminName}</a> for access!",
|
||||
"heading": "Your user is successfully logged in but has no role assigned yet. Please contact your RedactManager administrator to assign appropriate roles.",
|
||||
"heading-with-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >your RedactManager administrator</a> to assign appropriate roles!",
|
||||
"heading-with-name": "Your user is successfully logged in but has no role assigned yet. Please contact {adminName} to assign appropriate roles.",
|
||||
"heading-with-name-and-link": "Your user is successfully logged in but has no role assigned yet. Please contact <a href={adminUrl} target=_blank >{adminName}</a> to assign appropriate roles.",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"by": "by",
|
||||
@ -476,6 +480,7 @@
|
||||
"analysisColor": "Analysis",
|
||||
"defaultColor": "Default Color",
|
||||
"dictionaryRequestColor": "Dictionary Request",
|
||||
"ignoredHintColor": "Ignored Hint",
|
||||
"manualRedactionColor": "Manual Redaction",
|
||||
"notRedacted": "Skipped",
|
||||
"previewColor": "Preview",
|
||||
@ -639,6 +644,9 @@
|
||||
"action": "Delete Dossier",
|
||||
"delete-failed": "Failed to delete dossier: {dossierName}"
|
||||
},
|
||||
"dossier-info": {
|
||||
"action": "Dossier Info"
|
||||
},
|
||||
"edit": {
|
||||
"action": "Edit Dossier"
|
||||
},
|
||||
@ -882,9 +890,11 @@
|
||||
"title": "There are no deleted documents."
|
||||
},
|
||||
"table-col-names": {
|
||||
"assignee": "Assignee",
|
||||
"deleted-on": "Deleted On",
|
||||
"name": "Name",
|
||||
"pages": "Pages",
|
||||
"status": "Status",
|
||||
"time-to-restore": "Time To Restore"
|
||||
},
|
||||
"table-header": {
|
||||
@ -1110,6 +1120,7 @@
|
||||
"deleted": "Deleted",
|
||||
"error": "Re-processing required",
|
||||
"full-reprocess": "Processing",
|
||||
"image-analyzing": "Image Analyzing",
|
||||
"indexing": "Processing",
|
||||
"new": "New",
|
||||
"ocr-processing": "OCR Processing",
|
||||
@ -1283,7 +1294,8 @@
|
||||
"header": {
|
||||
"dictionary": "Add to dictionary",
|
||||
"false-positive": "Set false positive",
|
||||
"force": "Force Redaction",
|
||||
"force-hint": "Force Hint",
|
||||
"force-redaction": "Force Redaction",
|
||||
"redaction": "Redaction",
|
||||
"request-dictionary": "Request add to dictionary",
|
||||
"request-false-positive": "Request false positive",
|
||||
@ -1306,19 +1318,19 @@
|
||||
"sep": "Sep."
|
||||
},
|
||||
"notification": {
|
||||
"assign-approver": "You have been assigned as approver for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in the <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"assign-reviewer": "You have been assigned as reviewer for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in the <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"assign-approver": "You have been assigned as approver for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"assign-reviewer": "You have been assigned as reviewer for <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"document-approved": " <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> has been approved!",
|
||||
"dossier-deleted": "Dossier: <b>{dossierName}</b> has been deleted!",
|
||||
"dossier-owner-removed": "<b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> owner removed!",
|
||||
"dossier-owner-set": " <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> owner changed to <b>{user}</b>!",
|
||||
"download-ready": "Your <b><a href='/ui/main/downloads', target=\"_blank\">download</a><b> is ready!",
|
||||
"download-ready": "Your <b><a href='/ui/main/downloads', target=\"_blank\">download</a></b> is ready!",
|
||||
"no-data": "You currently have no notifications",
|
||||
"unassigned-from-file": "You have been unassigned from <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in the <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"user-becomes-dossier-member": "<b>{user}</b> joined dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-demoted-to-reviewer": "<b>{user}</b> demoted to reviewer in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-promoted-to-approver": "<b>{user}</b> promoted to approver in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-removed-as-dossier-member": "<b>{user}</b> removed as a member of: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> !"
|
||||
"unassigned-from-file": "You have been unassigned from <b><a href=\"{fileHref}\" target=\"_blank\">{fileName}</a></b> in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a><b>!",
|
||||
"user-becomes-dossier-member": "You have been added to dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-demoted-to-reviewer": "You have been demoted to reviewer in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-promoted-to-approver": "You have been promoted to approver in dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b>!",
|
||||
"user-removed-as-dossier-member": "You have been removed as a member from dossier: <b><a href=\"{dossierHref}\" target=\"_blank\">{dossierName}</a></b> !"
|
||||
},
|
||||
"notifications": "Notifications",
|
||||
"notifications-screen": {
|
||||
|
||||
@ -1,5 +1,39 @@
|
||||
@use 'variables';
|
||||
|
||||
.monaco-diff-editor {
|
||||
.editor.original {
|
||||
right: 30px !important; // diffOverviewRuler size
|
||||
left: unset !important;
|
||||
}
|
||||
|
||||
.editor.modified {
|
||||
left: 0 !important;
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
.diffOverview {
|
||||
.diffOverviewRuler.original {
|
||||
left: 15px !important;
|
||||
}
|
||||
|
||||
.diffOverviewRuler.modified {
|
||||
right: 15px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-editor.disabled {
|
||||
cursor: default;
|
||||
|
||||
.monaco-mouse-cursor-text {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cursors-layer > .cursor {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.changed-row-marker {
|
||||
background: rgba(variables.$primary, 0.1);
|
||||
}
|
||||
|
||||
@ -32,25 +32,3 @@
|
||||
$iqser-yellow-2: vars.$yellow-2,
|
||||
$iqser-helpmode-primary: vars.$green-2
|
||||
);
|
||||
|
||||
.monaco-diff-editor {
|
||||
.editor.original {
|
||||
right: 30px !important; // diffOverviewRuler size
|
||||
left: unset !important;
|
||||
}
|
||||
|
||||
.editor.modified {
|
||||
left: 0 !important;
|
||||
width: unset !important;
|
||||
}
|
||||
|
||||
.diffOverview {
|
||||
.diffOverviewRuler.original {
|
||||
left: 15px !important;
|
||||
}
|
||||
|
||||
.diffOverviewRuler.modified {
|
||||
right: 15px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: com.atlassian.bamboo:bamboo-specs-api:8.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains:annotations:20.1.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.code.findbugs:jsr305:3.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.12.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.atlassian.bamboo:bamboo-specs:8.0.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.8.6" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-validator:commons-validator:1.7" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-beanutils:commons-beanutils:1.9.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-digester:commons-digester:2.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.28" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpclient:4.5.13" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.apache.httpcomponents:httpcore:4.4.13" level="project" />
|
||||
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.11" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.13.2" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.atlassian.bamboo</groupId>
|
||||
<artifactId>bamboo-specs-parent</artifactId>
|
||||
<version>8.0.2</version>
|
||||
<version>8.1.1</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
|
||||
@ -59,7 +59,8 @@ public class PlanSpec {
|
||||
public Plan createDockerBuildPlan() {
|
||||
return new Plan(project(), "Redaction UI", new BambooKey("UI"))
|
||||
.description("Docker build for Redaction UI.")
|
||||
.stages(new Stage("Build Stage").jobs(creatGinCloudPlatformImagesJob("red-ui")))
|
||||
.stages(new Stage("UI Build Stage")
|
||||
.jobs(creatGinCloudPlatformImagesJob("red-ui")))
|
||||
.stages(new Stage("Release")
|
||||
.manual(true)
|
||||
.jobs(createRelease()))
|
||||
@ -70,14 +71,12 @@ public class PlanSpec {
|
||||
}
|
||||
|
||||
public Job creatGinCloudPlatformImagesJob(String project) {
|
||||
return new Job("Build Job: " + project, new BambooKey(project.toUpperCase().replaceAll("-", "")))
|
||||
return new Job("Build Job UI" , new BambooKey("UIBUILD"))
|
||||
.tasks(
|
||||
new CleanWorkingDirectoryTask().description("My clean working directory task"),
|
||||
// Checkout
|
||||
new VcsCheckoutTask().description("Checkout Default Repository")
|
||||
.checkoutItems(new CheckoutItem().defaultRepository().path("redaction-ui")),
|
||||
new VcsCheckoutTask().description("Checkout UI Shared Lib")
|
||||
.checkoutItems(new CheckoutItem().repository("Shared Libraries / common-ui").path("common-ui")),
|
||||
|
||||
// Build
|
||||
new ScriptTask().description("Build")
|
||||
|
||||
@ -5,13 +5,8 @@ set -e
|
||||
imageName="nexus.iqser.com:5001/red/$PROJECT"
|
||||
dockerfileLocation="docker/$PROJECT/Dockerfile"
|
||||
|
||||
echo "submodule status"
|
||||
git submodule status
|
||||
commonUIVersion=$(git submodule status | awk '{ print $1 }' | sed 's|-||g')
|
||||
echo $commonUIVersion
|
||||
cd ../common-ui
|
||||
git checkout $commonUIVersion
|
||||
cd ../redaction-ui
|
||||
mv ../common-ui ./libs/
|
||||
|
||||
echo "On branch $bamboo_planRepository_branchName building project $PROJECT"
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
@ -9,4 +9,5 @@ export interface IColors {
|
||||
requestAdd?: string;
|
||||
requestRemove?: string;
|
||||
updatedColor?: string;
|
||||
ignoredHintColor?: string;
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ import { IFileAttributeConfig } from './file-attribute-config';
|
||||
|
||||
export interface IFileAttributesConfig {
|
||||
delimiter?: string;
|
||||
fileAttributeConfigs?: IFileAttributeConfig[];
|
||||
encoding?: string;
|
||||
filenameMappingColumnHeaderName?: string;
|
||||
|
||||
fileAttributeConfigs?: IFileAttributeConfig[];
|
||||
}
|
||||
|
||||
@ -93,9 +93,7 @@ export class File extends Entity<IFile> implements IFile {
|
||||
this.hasSuggestions = !!file.hasSuggestions;
|
||||
|
||||
this.statusSort = StatusSorter[this.workflowStatus];
|
||||
if (this.lastUpdated && this.lastOCRTime) {
|
||||
this.cacheIdentifier = btoa((this.lastUploaded ?? '') + this.lastOCRTime);
|
||||
}
|
||||
this.cacheIdentifier = btoa((this.lastUploaded ?? '') + (this.lastOCRTime ?? ''));
|
||||
this.hintsOnly = this.hasHints && !this.hasRedactions;
|
||||
this.hasNone = !this.hasRedactions && !this.hasHints && !this.hasSuggestions;
|
||||
this.isPending = this.processingStatus === ProcessingFileStatuses.UNPROCESSED;
|
||||
|
||||
@ -14,6 +14,7 @@ export const ProcessingFileStatuses = {
|
||||
DELETED: 'DELETED',
|
||||
ERROR: 'ERROR',
|
||||
FULLREPROCESS: 'FULLREPROCESS',
|
||||
IMAGE_ANALYZING: 'IMAGE_ANALYZING',
|
||||
INDEXING: 'INDEXING',
|
||||
OCR_PROCESSING: 'OCR_PROCESSING',
|
||||
PROCESSED: 'PROCESSED',
|
||||
@ -28,6 +29,7 @@ export const isProcessingStatuses: List<ProcessingFileStatus> = [
|
||||
ProcessingFileStatuses.REPROCESS,
|
||||
ProcessingFileStatuses.FULLREPROCESS,
|
||||
ProcessingFileStatuses.OCR_PROCESSING,
|
||||
ProcessingFileStatuses.IMAGE_ANALYZING,
|
||||
ProcessingFileStatuses.INDEXING,
|
||||
ProcessingFileStatuses.PROCESSING,
|
||||
] as const;
|
||||
|
||||
@ -3,6 +3,7 @@ export type DefaultColorType =
|
||||
| 'defaultColor'
|
||||
| 'dictionaryRequestColor'
|
||||
| 'manualRedactionColor'
|
||||
| 'ignoredHintColor'
|
||||
| 'notRedacted'
|
||||
| 'previewColor'
|
||||
| 'requestAdd'
|
||||
|
||||
44
package.json
44
package.json
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "redaction",
|
||||
"version": "3.115.0",
|
||||
"version": "3.140.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "nx build",
|
||||
"build-lint-all": "ng lint --project=red-ui --fix && ng lint --project=common-ui --fix && ng build --project=red-ui --configuration production --base-href /ui/",
|
||||
"build-lint-all": "ng build --project=red-ui --configuration production --base-href /ui/",
|
||||
"build-paligo-styles": "mkdir -p dist/paligo-styles && sass --load-path=. paligo-styles/style.scss > dist/paligo-styles/redacto-theme.css",
|
||||
"i18n:extract": "ngx-translate-extract --input ./apps/red-ui/src ./libs/common-ui/src --output apps/red-ui/src/assets/i18n/en.json --clean --sort --format namespaced-json && prettier apps/red-ui/src/assets/i18n/*.json --write",
|
||||
"postinstall": "ngcc --properties es2015 browser module main",
|
||||
@ -23,36 +23,36 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "13.0.2",
|
||||
"@angular/cdk": "13.0.2",
|
||||
"@angular/common": "13.0.2",
|
||||
"@angular/compiler": "13.0.2",
|
||||
"@angular/core": "13.0.2",
|
||||
"@angular/forms": "13.0.2",
|
||||
"@angular/material": "13.0.2",
|
||||
"@angular/animations": "13.1.1",
|
||||
"@angular/cdk": "13.1.1",
|
||||
"@angular/common": "13.1.1",
|
||||
"@angular/compiler": "13.1.1",
|
||||
"@angular/core": "13.1.1",
|
||||
"@angular/forms": "13.1.1",
|
||||
"@angular/material": "13.1.1",
|
||||
"@angular/material-moment-adapter": "^13.0.2",
|
||||
"@angular/platform-browser": "13.0.2",
|
||||
"@angular/platform-browser-dynamic": "13.0.2",
|
||||
"@angular/router": "13.0.2",
|
||||
"@angular/service-worker": "13.0.2",
|
||||
"@angular/platform-browser": "13.1.1",
|
||||
"@angular/platform-browser-dynamic": "13.1.1",
|
||||
"@angular/router": "13.1.1",
|
||||
"@angular/service-worker": "13.1.1",
|
||||
"@biesbjerg/ngx-translate-extract-marker": "^1.0.0",
|
||||
"@materia-ui/ngx-monaco-editor": "^6.0.0-beta.1",
|
||||
"@ngx-translate/core": "^13.0.0",
|
||||
"@ngx-translate/http-loader": "^6.0.0",
|
||||
"@ngx-translate/core": "^14.0.0",
|
||||
"@ngx-translate/http-loader": "^7.0.0",
|
||||
"@nrwl/angular": "13.2.3",
|
||||
"@pdftron/webviewer": "8.2.0",
|
||||
"@swimlane/ngx-charts": "^17.0.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"keycloak-angular": "^8.4.0",
|
||||
"keycloak-js": "15.0.2",
|
||||
"keycloak-angular": "^9.0.0",
|
||||
"keycloak-js": "^16.0.0",
|
||||
"lodash.orderby": "^4.6.0",
|
||||
"messageformat": "^2.3.0",
|
||||
"moment": "^2.29.1",
|
||||
"monaco-editor": "^0.30.1",
|
||||
"ngx-color-picker": "^11.0.0",
|
||||
"ngx-toastr": "^14.1.3",
|
||||
"ngx-translate-messageformat-compiler": "^4.10.0",
|
||||
"ngx-translate-messageformat-compiler": "^4.11.0",
|
||||
"papaparse": "^5.3.1",
|
||||
"rxjs": "~6.6.7",
|
||||
"sass": "^1.39.2",
|
||||
@ -62,13 +62,13 @@
|
||||
"zone.js": "0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "13.0.3",
|
||||
"@angular-devkit/build-angular": "13.1.2",
|
||||
"@angular-eslint/eslint-plugin": "13.0.1",
|
||||
"@angular-eslint/eslint-plugin-template": "13.0.1",
|
||||
"@angular-eslint/template-parser": "13.0.1",
|
||||
"@angular/cli": "13.0.3",
|
||||
"@angular/compiler-cli": "13.0.2",
|
||||
"@angular/language-service": "13.0.2",
|
||||
"@angular/cli": "13.1.2",
|
||||
"@angular/compiler-cli": "13.1.1",
|
||||
"@angular/language-service": "13.1.1",
|
||||
"@nrwl/cli": "13.2.3",
|
||||
"@nrwl/cypress": "13.2.3",
|
||||
"@nrwl/eslint-plugin-nx": "13.2.3",
|
||||
|
||||
Binary file not shown.
@ -9,10 +9,10 @@
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"target": "es2015",
|
||||
"module": "esnext",
|
||||
"target": "es2017",
|
||||
"module": "es2020",
|
||||
"typeRoots": ["node_modules/@types"],
|
||||
"lib": ["es2019", "dom"],
|
||||
"lib": ["es2020", "dom"],
|
||||
"skipLibCheck": true,
|
||||
"skipDefaultLibCheck": true,
|
||||
"baseUrl": ".",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user