RED-4416: Annotation colors
This commit is contained in:
parent
d9435b8b4d
commit
29d1fc7653
@ -29,6 +29,7 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
typeValue: string;
|
||||
recategorizationType: string;
|
||||
color: string;
|
||||
dictionary: Dictionary;
|
||||
comments: IComment[] = [];
|
||||
firstTopLeftPoint: IPoint;
|
||||
annotationId: string;
|
||||
@ -102,7 +103,7 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
}
|
||||
|
||||
get colorKey(): KeysOf<Dictionary> {
|
||||
return this.isSkipped ? 'skippedHexColor' : this.isRecommendation ? 'recommendationHexColor' : 'hexColor';
|
||||
return this.isSkipped || this.isIgnoredHint ? 'skippedHexColor' : this.isRecommendation ? 'recommendationHexColor' : 'hexColor';
|
||||
}
|
||||
|
||||
get isSkipped() {
|
||||
@ -154,7 +155,13 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
}
|
||||
|
||||
get iconShape(): AnnotationIconType {
|
||||
return this.isRecommendation ? 'hexagon' : this.isHint ? 'circle' : this.isSuggestion ? 'rhombus' : 'square';
|
||||
return this.isRecommendation
|
||||
? 'hexagon'
|
||||
: this.isHint || this.isIgnoredHint
|
||||
? 'circle'
|
||||
: this.isSuggestion || this.isDeclinedSuggestion
|
||||
? 'rhombus'
|
||||
: 'square';
|
||||
}
|
||||
|
||||
get isIgnoredHint() {
|
||||
@ -262,7 +269,7 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
return annotationWrapper;
|
||||
}
|
||||
|
||||
static fromData(redactionLogEntry?: RedactionLogEntry) {
|
||||
static fromData(redactionLogEntry: RedactionLogEntry, dictionaries: Dictionary[]) {
|
||||
const annotationWrapper = new AnnotationWrapper();
|
||||
|
||||
annotationWrapper.annotationId = redactionLogEntry.id;
|
||||
@ -312,6 +319,11 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
|
||||
this._handleRecommendations(annotationWrapper, redactionLogEntry);
|
||||
annotationWrapper.typeLabel = this.#getTypeLabel(redactionLogEntry, annotationWrapper);
|
||||
|
||||
const type = annotationWrapper.isSuperTypeBasedColor ? annotationWrapper.superType : annotationWrapper.type;
|
||||
const dictionary = dictionaries.find(d => d.type === type);
|
||||
annotationWrapper.dictionary = dictionary;
|
||||
annotationWrapper.color = dictionary[annotationWrapper.colorKey] as string;
|
||||
|
||||
return annotationWrapper;
|
||||
}
|
||||
|
||||
|
||||
@ -11,13 +11,13 @@ export const SuperTypes = {
|
||||
SuggestionRemoveDictionary: 'suggestion-remove-dictionary',
|
||||
SuggestionAdd: 'suggestion-add',
|
||||
SuggestionRemove: 'suggestion-remove',
|
||||
IgnoredHint: 'ignored-hint',
|
||||
IgnoredHint: 'hint-ignored',
|
||||
Skipped: 'skipped',
|
||||
Redaction: 'redaction',
|
||||
ManualRedaction: 'manual-redaction',
|
||||
Recommendation: 'recommendation',
|
||||
Hint: 'hint',
|
||||
DeclinedSuggestion: 'declined-suggestion',
|
||||
DeclinedSuggestion: 'suggestion-declined',
|
||||
} as const;
|
||||
|
||||
export type SuperType = ValuesOf<typeof SuperTypes>;
|
||||
|
||||
@ -122,7 +122,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
this._permissionsService.isApprover(dossier),
|
||||
this._userService.currentUser,
|
||||
this.annotations,
|
||||
this._dictionariesMapService.get(dossier.dossierTemplateId),
|
||||
this._state.dictionaries,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,25 @@
|
||||
<div class="details">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<redaction-annotation-icon
|
||||
[color]="annotation.color"
|
||||
[label]="annotation.isHighlight ? '' : annotation.superType[0].toUpperCase()"
|
||||
[type]="annotation.iconShape"
|
||||
class="mt-6 mr-10"
|
||||
></redaction-annotation-icon>
|
||||
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong
|
||||
> <strong *ngIf="annotation.pending && !annotation.isSuggestion" class="pending-analysis">{{
|
||||
'annotation.pending' | translate
|
||||
}}</strong>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
|
||||
<strong *ngIf="annotation.pending && !annotation.isSuggestion" class="pending-analysis">
|
||||
{{ 'annotation.pending' | translate }}
|
||||
</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation.type !== 'manual' && !annotation.isHighlight">
|
||||
<strong>
|
||||
<span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ (dictionary$ | async).label }}
|
||||
>:
|
||||
</strong>
|
||||
{{ annotation.dictionary.label }}
|
||||
</div>
|
||||
<div *ngIf="annotation.shortContent && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.shortContent }}
|
||||
|
||||
@ -9,11 +9,6 @@
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.pending-analysis {
|
||||
color: var(--iqser-red-1);
|
||||
}
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { Dictionary, DOSSIER_ID } from '@red/domain';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-card',
|
||||
@ -15,34 +8,9 @@ import { DictionaryService } from '@services/entity-services/dictionary.service'
|
||||
styleUrls: ['./annotation-card.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AnnotationCardComponent implements OnChanges {
|
||||
export class AnnotationCardComponent {
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
@Input() isSelected = false;
|
||||
readonly dictionary$ = new BehaviorSubject<Dictionary>(undefined);
|
||||
readonly #dossierId: string;
|
||||
readonly #dossierTemplateId: string;
|
||||
|
||||
constructor(
|
||||
route: ActivatedRoute,
|
||||
dossiersService: DossiersService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _state: FilePreviewStateService,
|
||||
) {
|
||||
this.#dossierId = route.snapshot.paramMap.get(DOSSIER_ID);
|
||||
this.#dossierTemplateId = dossiersService.find(this.#dossierId).dossierTemplateId;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.annotation) {
|
||||
if (this.annotation.type !== 'manual' && !this.annotation.isHighlight) {
|
||||
const dictionary =
|
||||
this.annotation.type === 'dossier_redaction'
|
||||
? this._state.dossierDictionary
|
||||
: this._dictionariesMapService.getDictionary(this.annotation.type, this.#dossierTemplateId);
|
||||
this.dictionary$.next(dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
constructor(readonly multiSelectService: MultiSelectService) {}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div>
|
||||
<redaction-type-annotation-icon [annotation]="annotation" class="mr-8"></redaction-type-annotation-icon>
|
||||
<redaction-annotation-icon [color]="annotation.color" [label]="''" [type]="'square'" class="mr-8"></redaction-annotation-icon>
|
||||
<span [translateParams]="highlightGroup" [translate]="'highlights'" class="all-caps-label"></span>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<redaction-annotation-icon [color]="color" [label]="label" [type]="type"></redaction-annotation-icon>
|
||||
@ -1,53 +0,0 @@
|
||||
import { Component, Input, OnChanges } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-type-annotation-icon',
|
||||
templateUrl: './type-annotation-icon.component.html',
|
||||
styleUrls: ['./type-annotation-icon.component.scss'],
|
||||
})
|
||||
export class TypeAnnotationIconComponent implements OnChanges {
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
|
||||
label: string;
|
||||
color: string;
|
||||
type: 'square' | 'rhombus' | 'circle' | 'hexagon';
|
||||
|
||||
constructor(private _dictionariesMapService: DictionariesMapService, readonly screenStateService: FilePreviewStateService) {}
|
||||
|
||||
private get _dossierTemplateId(): string {
|
||||
return this.screenStateService.dossierTemplateId;
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
if (!this.annotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { isHighlight, isSuggestion, isRecommendation, isSkipped, isDeclinedSuggestion, isHint, isIgnoredHint } = this.annotation;
|
||||
|
||||
if (isHighlight) {
|
||||
this.color = this.annotation.color;
|
||||
} else {
|
||||
const type = this.annotation.isSuperTypeBasedColor ? this.annotation.superType : this.annotation.type;
|
||||
if (type === 'dossier_redaction') {
|
||||
this.color = this.screenStateService.dossierDictionary[this.annotation.colorKey] as string;
|
||||
} else {
|
||||
// todo: use annotation.colorKey
|
||||
this.color = this._dictionariesMapService.getDictionaryColor(
|
||||
type,
|
||||
this._dossierTemplateId,
|
||||
isRecommendation,
|
||||
isSkipped || isIgnoredHint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
this.type =
|
||||
isSuggestion || isDeclinedSuggestion ? 'rhombus' : isHint || isIgnoredHint ? 'circle' : isRecommendation ? 'hexagon' : 'square';
|
||||
|
||||
this.label = isHighlight ? '' : isSuggestion || isDeclinedSuggestion || isSkipped ? 'S' : this.annotation.type[0].toUpperCase();
|
||||
}
|
||||
}
|
||||
@ -97,7 +97,10 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
this.isRectangle && !!this.form.get('multiplePages').value
|
||||
? this.#getRectangles()
|
||||
: [this.data.manualRedactionEntryWrapper];
|
||||
this._dialogRef.close(annotations);
|
||||
this._dialogRef.close({
|
||||
annotations,
|
||||
dictionary: this.possibleDictionaries.find(d => d.type === this.form.get('dictionary').value),
|
||||
});
|
||||
} catch (e) {
|
||||
this._toaster.error(_('manual-annotation.dialog.error'));
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from './services/annotation-processing.service';
|
||||
import { File, ViewMode, ViewModes } from '@red/domain';
|
||||
import { Dictionary, File, ViewMode, ViewModes } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, firstValueFrom, from, of, pairwise } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
@ -194,12 +194,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._loadingService.start();
|
||||
this._annotationManager.hide(annotations);
|
||||
const highlights = await this._fileDataService.loadTextHighlights();
|
||||
await this._annotationDrawService.draw(
|
||||
highlights,
|
||||
this.state.dossierTemplateId,
|
||||
this._skippedService.hideSkipped,
|
||||
this.state.dossierDictionary,
|
||||
);
|
||||
await this._annotationDrawService.draw(highlights, this._skippedService.hideSkipped, this.state.dictionaries);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
@ -256,16 +251,17 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
'manualAnnotation',
|
||||
null,
|
||||
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file },
|
||||
(wrappers: ManualRedactionEntryWrapper[]) => {
|
||||
(result: { annotations: ManualRedactionEntryWrapper[]; dictionary?: Dictionary }) => {
|
||||
const selectedAnnotations = this._annotationManager.selected;
|
||||
if (selectedAnnotations.length > 0) {
|
||||
this._annotationManager.delete([selectedAnnotations[0].Id]);
|
||||
}
|
||||
const manualRedactionService = this._injector.get(ManualRedactionService);
|
||||
const add$ = manualRedactionService.addAnnotation(
|
||||
wrappers.map(w => w.manualRedactionEntry).filter(e => e.positions[0].page <= file.numberOfPages),
|
||||
result.annotations.map(w => w.manualRedactionEntry).filter(e => e.positions[0].page <= file.numberOfPages),
|
||||
this.dossierId,
|
||||
this.fileId,
|
||||
result.dictionary?.label,
|
||||
);
|
||||
const filesService = this._injector.get(FilesService);
|
||||
const addAndReload$ = add$.pipe(switchMap(() => filesService.reload(this.dossierId, file)));
|
||||
@ -600,12 +596,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._handleDeltaAnnotationFilters(currentFilters);
|
||||
}
|
||||
|
||||
await this._annotationDrawService.draw(
|
||||
newAnnotations,
|
||||
this.state.dossierTemplateId,
|
||||
this._skippedService.hideSkipped,
|
||||
this.state.dossierDictionary,
|
||||
);
|
||||
await this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped, this.state.dictionaries);
|
||||
}
|
||||
|
||||
private _handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {
|
||||
|
||||
@ -14,7 +14,6 @@ import { PageExclusionComponent } from './components/page-exclusion/page-exclusi
|
||||
import { AnnotationActionsComponent } from './components/annotation-actions/annotation-actions.component';
|
||||
import { CommentsComponent } from './components/comments/comments.component';
|
||||
import { DocumentInfoComponent } from './components/document-info/document-info.component';
|
||||
import { TypeAnnotationIconComponent } from './components/type-annotation-icon/type-annotation-icon.component';
|
||||
import { OverlayModule } from '@angular/cdk/overlay';
|
||||
import { ViewSwitchComponent } from './components/view-switch/view-switch.component';
|
||||
import { UserManagementComponent } from './components/user-management/user-management.component';
|
||||
@ -72,7 +71,6 @@ const components = [
|
||||
AnnotationActionsComponent,
|
||||
CommentsComponent,
|
||||
DocumentInfoComponent,
|
||||
TypeAnnotationIconComponent,
|
||||
ViewSwitchComponent,
|
||||
UserManagementComponent,
|
||||
AnnotationReferencesListComponent,
|
||||
|
||||
@ -235,12 +235,7 @@ export class AnnotationActionsService {
|
||||
annotationWrapper.resizing = false;
|
||||
|
||||
this._annotationManager.delete(annotationWrapper);
|
||||
await this._annotationDrawService.draw(
|
||||
[annotationWrapper],
|
||||
this._state.dossierTemplateId,
|
||||
this._skippedService.hideSkipped,
|
||||
this._state.dossierDictionary,
|
||||
);
|
||||
await this._annotationDrawService.draw([annotationWrapper], this._skippedService.hideSkipped, this._state.dictionaries);
|
||||
this._annotationManager.deselect();
|
||||
await this._fileDataService.annotationsChanged();
|
||||
}
|
||||
@ -276,13 +271,7 @@ export class AnnotationActionsService {
|
||||
annotation.ReadOnly = false;
|
||||
annotation.Hidden = false;
|
||||
annotation.disableRotationControl();
|
||||
const dossierTemplateId = this._state.dossierTemplateId;
|
||||
annotation.FillColor = this._annotationDrawService.getAndConvertColor(
|
||||
annotationWrapper.superType,
|
||||
dossierTemplateId,
|
||||
this._state.dossierDictionary,
|
||||
annotationWrapper.type,
|
||||
);
|
||||
annotation.FillColor = this._annotationDrawService.convertColor(annotationWrapper.color);
|
||||
annotation.StrokeColor = annotation.FillColor;
|
||||
annotation.Opacity = 0.6;
|
||||
annotation.StrokeThickness = 1;
|
||||
|
||||
@ -66,18 +66,13 @@ export class AnnotationProcessingService {
|
||||
// top level filter
|
||||
if (topLevelFilter) {
|
||||
this._createParentFilter(a.isHighlight ? a.filterKey : a.superType, filterMap, filters, a.isHighlight, {
|
||||
color: a.isHighlight ? a.color : null,
|
||||
color: a.color,
|
||||
shortLabel: a.isHighlight ? '' : null,
|
||||
shape: a.iconShape,
|
||||
});
|
||||
} else {
|
||||
let parentFilter = filterMap.get(a.superType);
|
||||
|
||||
const dictionary =
|
||||
a.type === 'dossier_redaction'
|
||||
? this.#state.dossierDictionary
|
||||
: this.#dictionariesMapService.getDictionary(a.type, this.#state.dossierTemplateId);
|
||||
|
||||
if (!parentFilter) {
|
||||
parentFilter = this._createParentFilter(a.superType, filterMap, filters, false, {
|
||||
shape: a.iconShape,
|
||||
@ -85,11 +80,11 @@ export class AnnotationProcessingService {
|
||||
}
|
||||
const childFilter: IFilter = {
|
||||
id: a.filterKey,
|
||||
label: dictionary.label,
|
||||
label: a.dictionary.label,
|
||||
checked: false,
|
||||
matches: 1,
|
||||
metadata: {
|
||||
color: dictionary[a.colorKey],
|
||||
color: a.color,
|
||||
shape: a.iconShape,
|
||||
},
|
||||
skipTranslation: true,
|
||||
|
||||
@ -146,7 +146,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
|
||||
|
||||
#buildAnnotations(redactionLog: IRedactionLog, file: File) {
|
||||
const entries: RedactionLogEntry[] = this.#convertData(redactionLog, file);
|
||||
const annotations = entries.map(entry => AnnotationWrapper.fromData(entry));
|
||||
const annotations = entries.map(entry => AnnotationWrapper.fromData(entry, this._state.dictionaries));
|
||||
|
||||
return annotations.filter(ann => ann.manual || !file.excludedPages.includes(ann.pageNumber));
|
||||
}
|
||||
@ -154,11 +154,10 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
|
||||
#convertData(redactionLog: IRedactionLog, file: File): RedactionLogEntry[] {
|
||||
let result: RedactionLogEntry[] = [];
|
||||
const sourceIdAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
|
||||
const dictionaries = this._dictionariesMapService.get(this._state.dossierTemplateId);
|
||||
|
||||
redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
||||
const changeLogValues = this.#getChangeLogValues(redactionLogEntry, file);
|
||||
const dictionary = dictionaries.find(dict => dict.type === redactionLogEntry.type);
|
||||
const dictionary = this._state.dictionaries.find(dict => dict.type === redactionLogEntry.type);
|
||||
if (!dictionary) {
|
||||
this.missingTypes.add(redactionLogEntry.type);
|
||||
return;
|
||||
|
||||
@ -14,6 +14,7 @@ import { FilesService } from '@services/files/files.service';
|
||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||
import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angular/common/http';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service';
|
||||
|
||||
const ONE_MEGABYTE = 1024 * 1024;
|
||||
|
||||
@ -55,6 +56,7 @@ export class FilePreviewStateService {
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
) {
|
||||
@ -83,6 +85,10 @@ export class FilePreviewStateService {
|
||||
return this.#dossierDictionary;
|
||||
}
|
||||
|
||||
get dictionaries(): Dictionary[] {
|
||||
return this._dictionariesMapService.get(this.dossierTemplateId).concat([this.dossierDictionary]);
|
||||
}
|
||||
|
||||
get blob(): Promise<Blob> {
|
||||
return firstValueFrom(this.blob$);
|
||||
}
|
||||
|
||||
@ -18,7 +18,6 @@ import { dictionaryActionsTranslations, manualRedactionActionsTranslations } fro
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
|
||||
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
@ -46,7 +45,6 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
protected readonly _injector: Injector,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _activeDossiersService: ActiveDossiersService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
) {
|
||||
super(_injector, 'manualRedaction');
|
||||
}
|
||||
@ -92,8 +90,8 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
return this.requestRecategorize(body, dossierId, fileId).pipe(this.#showToast('request-image-recategorization'));
|
||||
}
|
||||
|
||||
addAnnotation(requests: List<IAddRedactionRequest>, dossierId: string, fileId: string) {
|
||||
const toast = requests[0].addToDictionary ? this.#showAddToDictionaryToast(requests, dossierId) : this.#showToast('add');
|
||||
addAnnotation(requests: List<IAddRedactionRequest>, dossierId: string, fileId: string, dictionaryLabel?: string) {
|
||||
const toast = requests[0].addToDictionary ? this.#showAddToDictionaryToast(requests, dictionaryLabel) : this.#showToast('add');
|
||||
if (this._permissionsService.isApprover(this.#dossier(dossierId))) {
|
||||
return this.add(requests, dossierId, fileId).pipe(toast);
|
||||
}
|
||||
@ -291,7 +289,7 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
});
|
||||
}
|
||||
|
||||
#showAddToDictionaryToast(body: List<IAddRedactionRequest>, dossierId: string) {
|
||||
#showAddToDictionaryToast(body: List<IAddRedactionRequest>, dictionaryLabel?: string) {
|
||||
return tap({
|
||||
next: () => this._toaster.success(getDictionaryMessage('add'), { positionClass: 'toast-file-preview' }),
|
||||
error: (error: HttpErrorResponse) => {
|
||||
@ -299,8 +297,7 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
||||
this._toaster.error(getDictionaryMessage('add', true, isConflict), {
|
||||
error,
|
||||
params: {
|
||||
dictionaryName: this._dictionariesMapService.getDictionary(body[0].type, this.#dossier(dossierId).dossierTemplateId)
|
||||
.label,
|
||||
dictionaryName: dictionaryLabel,
|
||||
content: body[0].value,
|
||||
},
|
||||
positionClass: 'toast-file-preview',
|
||||
|
||||
@ -144,9 +144,8 @@ export class PdfAnnotationActionsService {
|
||||
#getAnnotationsPermissions(annotations: AnnotationWrapper[]) {
|
||||
const dossier = this.#state.dossier;
|
||||
const isApprover = this.#permissionsService.isApprover(dossier);
|
||||
const dictionaries = this.#dictionariesMapService.get(dossier.dossierTemplateId);
|
||||
|
||||
const permissions = annotations.map(a => AnnotationPermissions.forUser(isApprover, this.#currentUser, a, dictionaries));
|
||||
const permissions = annotations.map(a => AnnotationPermissions.forUser(isApprover, this.#currentUser, a, this.#state.dictionaries));
|
||||
return {
|
||||
canResize: permissions.length === 1 && permissions[0].canResizeAnnotation,
|
||||
canChangeLegalBasis: permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true),
|
||||
|
||||
@ -148,13 +148,7 @@ export class PdfProxyService {
|
||||
}
|
||||
|
||||
private _configureElements() {
|
||||
const dossierTemplateId = this._state.dossierTemplateId;
|
||||
const color = this._annotationDrawService.getAndConvertColor(
|
||||
dossierTemplateId,
|
||||
dossierTemplateId,
|
||||
this._state.dossierDictionary,
|
||||
'manual',
|
||||
);
|
||||
const color = this._annotationDrawService.convertColor(this._state.dictionaries.find(d => d.type === 'manual').hexColor);
|
||||
this._documentViewer.setRectangleToolStyles(color);
|
||||
}
|
||||
|
||||
|
||||
@ -32,18 +32,14 @@ export class AnnotationDrawService {
|
||||
private readonly _documentViewer: REDDocumentViewer,
|
||||
) {}
|
||||
|
||||
async draw(annotations: List<AnnotationWrapper>, dossierTemplateId: string, hideSkipped: boolean, dossierDictionary: Dictionary) {
|
||||
async draw(annotations: List<AnnotationWrapper>, hideSkipped: boolean, dictionaries: Dictionary[]) {
|
||||
try {
|
||||
await this._draw(annotations, dossierTemplateId, hideSkipped, dossierDictionary);
|
||||
await this._draw(annotations, hideSkipped, dictionaries);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
getAndConvertColor(superType: string, dossierTemplateId: string, dossierDictionary: Dictionary, type?: string) {
|
||||
return this.convertColor(this.#getColor(superType, dossierTemplateId, type, dossierDictionary));
|
||||
}
|
||||
|
||||
convertColor(hexColor: string) {
|
||||
return this._pdf.color(hexToRgb(hexColor));
|
||||
}
|
||||
@ -64,45 +60,10 @@ export class AnnotationDrawService {
|
||||
return this._pdf.quad(x1, y1, x2, y2, x3, y3, x4, y4);
|
||||
}
|
||||
|
||||
#getColor(superType: string, dossierTemplateId: string, type: string, dossierDictionary: Dictionary) {
|
||||
let color: string;
|
||||
let dictionary: Dictionary;
|
||||
|
||||
if (type === 'dossier_redaction') {
|
||||
dictionary = dossierDictionary;
|
||||
} else {
|
||||
dictionary = this._dictionariesMapService.getDictionary(type, dossierTemplateId);
|
||||
}
|
||||
|
||||
switch (superType) {
|
||||
case SuperTypes.Hint:
|
||||
case SuperTypes.Redaction:
|
||||
color = dictionary.hexColor;
|
||||
break;
|
||||
case SuperTypes.Recommendation:
|
||||
color = dictionary.recommendationHexColor;
|
||||
break;
|
||||
case SuperTypes.Skipped:
|
||||
case SuperTypes.IgnoredHint:
|
||||
color = dictionary.skippedHexColor;
|
||||
break;
|
||||
default:
|
||||
color = dictionary.hexColor;
|
||||
break;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
private async _draw(
|
||||
annotationWrappers: List<AnnotationWrapper>,
|
||||
dossierTemplateId: string,
|
||||
hideSkipped: boolean,
|
||||
dossierDictionary: Dictionary,
|
||||
) {
|
||||
private async _draw(annotationWrappers: List<AnnotationWrapper>, hideSkipped: boolean, dictionaries: Dictionary[]) {
|
||||
const totalPages = await firstValueFrom(this._pdf.totalPages$);
|
||||
const annotations = annotationWrappers
|
||||
.map(annotation => this._computeAnnotation(annotation, dossierTemplateId, hideSkipped, totalPages, dossierDictionary))
|
||||
.map(annotation => this._computeAnnotation(annotation, hideSkipped, totalPages, dictionaries))
|
||||
.filterTruthy();
|
||||
const documentLoaded = await firstValueFrom(this._documentViewer.loaded$);
|
||||
if (!documentLoaded) {
|
||||
@ -114,27 +75,22 @@ export class AnnotationDrawService {
|
||||
const { dossierId, fileId } = this._pdf;
|
||||
const sectionsGrid$ = this._redactionLogService.getSectionGrid(dossierId, fileId);
|
||||
const sectionsGrid = await firstValueFrom(sectionsGrid$).catch(() => ({ rectanglesPerPage: {} }));
|
||||
await this._drawSections(sectionsGrid, dossierTemplateId, dossierDictionary);
|
||||
await this._drawSections(sectionsGrid, dictionaries);
|
||||
}
|
||||
}
|
||||
|
||||
private async _drawSections(sectionGrid: ISectionGrid, dossierTemplateId: string, dossierDictionary: Dictionary) {
|
||||
private async _drawSections(sectionGrid: ISectionGrid, dictionaries: Dictionary[]) {
|
||||
const sections: Core.Annotations.RectangleAnnotation[] = [];
|
||||
for (const page of Object.keys(sectionGrid.rectanglesPerPage)) {
|
||||
const sectionRectangles = sectionGrid.rectanglesPerPage[page];
|
||||
sectionRectangles.forEach(sectionRectangle => {
|
||||
sections.push(this._computeSection(dossierTemplateId, parseInt(page, 10), sectionRectangle, dossierDictionary));
|
||||
sections.push(this._computeSection(parseInt(page, 10), sectionRectangle, dictionaries));
|
||||
});
|
||||
}
|
||||
await this._annotationManager.add(sections);
|
||||
}
|
||||
|
||||
private _computeSection(
|
||||
dossierTemplateId: string,
|
||||
pageNumber: number,
|
||||
sectionRectangle: ISectionRectangle,
|
||||
dossierDictionary: Dictionary,
|
||||
) {
|
||||
private _computeSection(pageNumber: number, sectionRectangle: ISectionRectangle, dictionaries: Dictionary[]) {
|
||||
const rectangleAnnot = this._pdf.rectangle();
|
||||
const pageHeight = this._documentViewer.getHeight(pageNumber);
|
||||
const rectangle: IRectangle = {
|
||||
@ -149,19 +105,13 @@ export class AnnotationDrawService {
|
||||
rectangleAnnot.Width = rectangle.width + 2;
|
||||
rectangleAnnot.Height = rectangle.height + 2;
|
||||
rectangleAnnot.ReadOnly = true;
|
||||
rectangleAnnot.StrokeColor = this.getAndConvertColor('analysis', dossierTemplateId, dossierDictionary, 'analysis');
|
||||
rectangleAnnot.StrokeColor = this.convertColor(dictionaries.find(d => d.type === 'analysis').hexColor);
|
||||
rectangleAnnot.StrokeThickness = 1;
|
||||
|
||||
return rectangleAnnot;
|
||||
}
|
||||
|
||||
private _computeAnnotation(
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
dossierTemplateId: string,
|
||||
hideSkipped: boolean,
|
||||
totalPages: number,
|
||||
dossierDictionary: Dictionary,
|
||||
) {
|
||||
private _computeAnnotation(annotationWrapper: AnnotationWrapper, hideSkipped: boolean, totalPages: number, dictionaries: Dictionary[]) {
|
||||
const pageNumber = this._pdf.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
|
||||
if (pageNumber > totalPages) {
|
||||
// skip imported annotations from files that have more pages than the current one
|
||||
@ -190,12 +140,7 @@ export class AnnotationDrawService {
|
||||
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
|
||||
annotation.setContents(annotationWrapper.content);
|
||||
annotation.PageNumber = pageNumber;
|
||||
annotation.StrokeColor = this.getAndConvertColor(
|
||||
annotationWrapper.superType,
|
||||
dossierTemplateId,
|
||||
dossierDictionary,
|
||||
annotationWrapper.type,
|
||||
);
|
||||
annotation.StrokeColor = this.convertColor(annotationWrapper.color);
|
||||
annotation.Id = annotationWrapper.id;
|
||||
annotation.ReadOnly = true;
|
||||
|
||||
@ -210,11 +155,8 @@ export class AnnotationDrawService {
|
||||
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
|
||||
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
|
||||
annotation.setCustomData('opacity', String(annotation.Opacity));
|
||||
annotation.setCustomData('redactionColor', String(this.#getColor('redaction', dossierTemplateId, 'preview', dossierDictionary)));
|
||||
annotation.setCustomData(
|
||||
'annotationColor',
|
||||
String(this.#getColor(annotationWrapper.superType, dossierTemplateId, annotationWrapper.type, dossierDictionary)),
|
||||
);
|
||||
annotation.setCustomData('redactionColor', String(dictionaries.find(d => d.type === 'preview').hexColor));
|
||||
annotation.setCustomData('annotationColor', String(annotationWrapper.color));
|
||||
|
||||
return annotation;
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ export class DictionariesMapService extends EntitiesMapService<Dictionary, IDict
|
||||
return this.get(dossierTemplateId, type) || this.get(dossierTemplateId, 'default');
|
||||
}
|
||||
|
||||
// todo is this necessary?
|
||||
getDictionaryColor(type: string, dossierTemplateId: string, isRecommendation = false, isSkipped = false) {
|
||||
const defaultColor = '#CCCCCC';
|
||||
if (!this.get(dossierTemplateId)) {
|
||||
|
||||
@ -237,12 +237,6 @@ export class DictionaryService extends EntitiesService<Dictionary, IDictionary>
|
||||
}),
|
||||
map(colors => {
|
||||
const virtualTypes = [
|
||||
{
|
||||
hexColor: colors.manualRedactionColor || FALLBACK_COLOR,
|
||||
type: 'dossier_redaction',
|
||||
hint: false,
|
||||
recommendation: false,
|
||||
},
|
||||
{ hexColor: colors.notRedacted || FALLBACK_COLOR, type: SuperTypes.DeclinedSuggestion },
|
||||
{ hexColor: colors.manualRedactionColor || FALLBACK_COLOR, type: 'manual' },
|
||||
{ hexColor: colors.ignoredHintColor || FALLBACK_COLOR, type: SuperTypes.IgnoredHint },
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 738c38cf5f13de9542facd84d8c70f9929b980e6
|
||||
Subproject commit f2f7283c9c97e30752fdd4cf56ca3b41999e6962
|
||||
Loading…
x
Reference in New Issue
Block a user