RED-4416: Annotation colors

This commit is contained in:
Adina Țeudan 2022-07-10 01:39:19 +03:00
parent 80cef8bad6
commit d9435b8b4d
25 changed files with 209 additions and 213 deletions

View File

@ -1,6 +1,16 @@
import { annotationTypesTranslations, SuggestionAddFalsePositive } from '@translations/annotation-types-translations'; import { annotationTypesTranslations, SuggestionAddFalsePositive } from '@translations/annotation-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Highlight, IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain'; import {
AnnotationIconType,
Dictionary,
Highlight,
IComment,
IManualChange,
IPoint,
IRectangle,
LogEntryStatus,
ManualRedactionType,
} from '@red/domain';
import { RedactionLogEntry } from '@models/file/redaction-log.entry'; import { RedactionLogEntry } from '@models/file/redaction-log.entry';
import { import {
FalsePositiveSuperTypes, FalsePositiveSuperTypes,
@ -10,7 +20,7 @@ import {
SuperType, SuperType,
SuperTypes, SuperTypes,
} from '@models/file/super-types'; } from '@models/file/super-types';
import { IListable, List } from '@iqser/common-ui'; import { IListable, KeysOf, List } from '@iqser/common-ui';
export class AnnotationWrapper implements IListable, Record<string, unknown> { export class AnnotationWrapper implements IListable, Record<string, unknown> {
[x: string]: unknown; [x: string]: unknown;
@ -91,6 +101,10 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
return this.isSuggestion || this.isDeclinedSuggestion; return this.isSuggestion || this.isDeclinedSuggestion;
} }
get colorKey(): KeysOf<Dictionary> {
return this.isSkipped ? 'skippedHexColor' : this.isRecommendation ? 'recommendationHexColor' : 'hexColor';
}
get isSkipped() { get isSkipped() {
return this.superType === SuperTypes.Skipped; return this.superType === SuperTypes.Skipped;
} }
@ -139,6 +153,10 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
return this.superType === SuperTypes.TextHighlight; return this.superType === SuperTypes.TextHighlight;
} }
get iconShape(): AnnotationIconType {
return this.isRecommendation ? 'hexagon' : this.isHint ? 'circle' : this.isSuggestion ? 'rhombus' : 'square';
}
get isIgnoredHint() { get isIgnoredHint() {
return this.superType === SuperTypes.IgnoredHint; return this.superType === SuperTypes.IgnoredHint;
} }

View File

@ -83,7 +83,11 @@
</div> </div>
<div class="cell center"> <div class="cell center">
<redaction-annotation-icon [dictionary]="dict" [type]="dict.hint ? 'circle' : 'square'"></redaction-annotation-icon> <redaction-annotation-icon
[color]="dict.hexColor"
[label]="dict.label.charAt(0)"
[type]="dict.hint ? 'circle' : 'square'"
></redaction-annotation-icon>
</div> </div>
<div class="cell"> <div class="cell">

View File

@ -19,10 +19,6 @@
margin-top: 16px; margin-top: 16px;
display: flex; display: flex;
align-items: center; align-items: center;
redaction-dictionary-annotation-icon {
margin-right: 8px;
}
} }
redaction-dictionary-manager { redaction-dictionary-manager {

View File

@ -11,7 +11,16 @@ import {
TableColumnConfig, TableColumnConfig,
WorkflowConfig, WorkflowConfig,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { Dossier, File, IFileAttributeConfig, ProcessingType, StatusSorter, WorkflowFileStatus, WorkflowFileStatuses } from '@red/domain'; import {
AnnotationShapeMap,
Dossier,
File,
IFileAttributeConfig,
ProcessingType,
StatusSorter,
WorkflowFileStatus,
WorkflowFileStatuses,
} from '@red/domain';
import { workflowFileStatusTranslations } from '@translations/file-status-translations'; import { workflowFileStatusTranslations } from '@translations/file-status-translations';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -277,6 +286,7 @@ export class ConfigService {
new NestedFilter({ new NestedFilter({
id: item, id: item,
label: workloadTranslations[item], label: workloadTranslations[item],
metadata: { shape: AnnotationShapeMap[item] },
}), }),
); );

View File

@ -1,6 +1,6 @@
import { Injectable, TemplateRef } from '@angular/core'; import { Injectable, TemplateRef } from '@angular/core';
import { ButtonConfig, IFilterGroup, INestedFilter, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui'; import { ButtonConfig, IFilterGroup, INestedFilter, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { Dossier, DossierTemplate, StatusSorter, User, WorkflowFileStatus } from '@red/domain'; import { AnnotationShapeMap, Dossier, DossierTemplate, StatusSorter, User, WorkflowFileStatus } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { UserPreferenceService } from '@services/user-preference.service'; import { UserPreferenceService } from '@services/user-preference.service';
@ -145,6 +145,7 @@ export class ConfigService {
new NestedFilter({ new NestedFilter({
id: type, id: type,
label: workloadTranslations[type], label: workloadTranslations[type],
metadata: { shape: AnnotationShapeMap[type] },
}), }),
); );

View File

@ -32,12 +32,17 @@ export class TypeAnnotationIconComponent implements OnChanges {
this.color = this.annotation.color; this.color = this.annotation.color;
} else { } else {
const type = this.annotation.isSuperTypeBasedColor ? this.annotation.superType : this.annotation.type; const type = this.annotation.isSuperTypeBasedColor ? this.annotation.superType : this.annotation.type;
this.color = this._dictionariesMapService.getDictionaryColor( if (type === 'dossier_redaction') {
type, this.color = this.screenStateService.dossierDictionary[this.annotation.colorKey] as string;
this._dossierTemplateId, } else {
isRecommendation, // todo: use annotation.colorKey
isSkipped || isIgnoredHint, this.color = this._dictionariesMapService.getDictionaryColor(
); type,
this._dossierTemplateId,
isRecommendation,
isSkipped || isIgnoredHint,
);
}
} }
this.type = this.type =

View File

@ -101,18 +101,5 @@
</ng-template> </ng-template>
<ng-template #annotationFilterTemplate let-filter="filter"> <ng-template #annotationFilterTemplate let-filter="filter">
<redaction-type-filter <redaction-type-filter [dossierTemplateId]="state.dossierTemplateId" [filter]="filter"></redaction-type-filter>
*ngIf="filter.topLevelFilter"
[dossierTemplateId]="state.dossierTemplateId"
[filter]="filter"
></redaction-type-filter>
<ng-container *ngIf="!filter.topLevelFilter">
<redaction-dictionary-annotation-icon
[dictionaryKey]="filter.id"
[dossierTemplateId]="state.dossierTemplateId"
></redaction-dictionary-annotation-icon>
{{ filter.label }}
</ng-container>
</ng-template> </ng-template>

View File

@ -53,10 +53,6 @@
} }
} }
::ng-deep redaction-dictionary-annotation-icon {
margin-right: 8px;
}
.analysis-progress { .analysis-progress {
padding: 12px 20px; padding: 12px 20px;
max-width: 400px; max-width: 400px;

View File

@ -194,7 +194,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._loadingService.start(); this._loadingService.start();
this._annotationManager.hide(annotations); this._annotationManager.hide(annotations);
const highlights = await this._fileDataService.loadTextHighlights(); const highlights = await this._fileDataService.loadTextHighlights();
await this._annotationDrawService.draw(highlights, this.state.dossierTemplateId, this._skippedService.hideSkipped); await this._annotationDrawService.draw(
highlights,
this.state.dossierTemplateId,
this._skippedService.hideSkipped,
this.state.dossierDictionary,
);
this._loadingService.stop(); this._loadingService.stop();
} }
} }
@ -595,7 +600,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._handleDeltaAnnotationFilters(currentFilters); this._handleDeltaAnnotationFilters(currentFilters);
} }
await this._annotationDrawService.draw(newAnnotations, this.state.dossierTemplateId, this._skippedService.hideSkipped); await this._annotationDrawService.draw(
newAnnotations,
this.state.dossierTemplateId,
this._skippedService.hideSkipped,
this.state.dossierDictionary,
);
} }
private _handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) { private _handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {

View File

@ -235,7 +235,12 @@ export class AnnotationActionsService {
annotationWrapper.resizing = false; annotationWrapper.resizing = false;
this._annotationManager.delete(annotationWrapper); this._annotationManager.delete(annotationWrapper);
await this._annotationDrawService.draw([annotationWrapper], this._state.dossierTemplateId, this._skippedService.hideSkipped); await this._annotationDrawService.draw(
[annotationWrapper],
this._state.dossierTemplateId,
this._skippedService.hideSkipped,
this._state.dossierDictionary,
);
this._annotationManager.deselect(); this._annotationManager.deselect();
await this._fileDataService.annotationsChanged(); await this._fileDataService.annotationsChanged();
} }
@ -275,6 +280,7 @@ export class AnnotationActionsService {
annotation.FillColor = this._annotationDrawService.getAndConvertColor( annotation.FillColor = this._annotationDrawService.getAndConvertColor(
annotationWrapper.superType, annotationWrapper.superType,
dossierTemplateId, dossierTemplateId,
this._state.dossierDictionary,
annotationWrapper.type, annotationWrapper.type,
); );
annotation.StrokeColor = annotation.FillColor; annotation.StrokeColor = annotation.FillColor;

View File

@ -65,27 +65,34 @@ export class AnnotationProcessingService {
} else { } else {
// top level filter // top level filter
if (topLevelFilter) { if (topLevelFilter) {
this._createParentFilter( this._createParentFilter(a.isHighlight ? a.filterKey : a.superType, filterMap, filters, a.isHighlight, {
a.isHighlight ? a.filterKey : a.superType, color: a.isHighlight ? a.color : null,
filterMap, shortLabel: a.isHighlight ? '' : null,
filters, shape: a.iconShape,
a.isHighlight, });
a.isHighlight ? a.color : null,
);
} else { } else {
let parentFilter = filterMap.get(a.superType); let parentFilter = filterMap.get(a.superType);
if (!parentFilter) {
parentFilter = this._createParentFilter(a.superType, filterMap, filters);
}
const dictionary = const dictionary =
a.type === 'dossier_redaction' a.type === 'dossier_redaction'
? this.#state.dossierDictionary ? this.#state.dossierDictionary
: this.#dictionariesMapService.getDictionary(a.type, this.#state.dossierTemplateId); : this.#dictionariesMapService.getDictionary(a.type, this.#state.dossierTemplateId);
if (!parentFilter) {
parentFilter = this._createParentFilter(a.superType, filterMap, filters, false, {
shape: a.iconShape,
});
}
const childFilter: IFilter = { const childFilter: IFilter = {
id: a.filterKey, id: a.filterKey,
label: dictionary.label, label: dictionary.label,
checked: false, checked: false,
matches: 1, matches: 1,
metadata: {
color: dictionary[a.colorKey],
shape: a.iconShape,
},
skipTranslation: true,
}; };
filterMap.set(a.filterKey, childFilter); filterMap.set(a.filterKey, childFilter);
parentFilter.children.push(new Filter(childFilter)); parentFilter.children.push(new Filter(childFilter));
@ -154,7 +161,7 @@ export class AnnotationProcessingService {
filterMap: Map<string, INestedFilter>, filterMap: Map<string, INestedFilter>,
filters: INestedFilter[], filters: INestedFilter[],
skipTranslation = false, skipTranslation = false,
color?: string, metadata?: Record<string, any>,
) { ) {
const filter: INestedFilter = new NestedFilter({ const filter: INestedFilter = new NestedFilter({
id: key, id: key,
@ -162,7 +169,7 @@ export class AnnotationProcessingService {
matches: 1, matches: 1,
label: skipTranslation ? key : annotationTypesTranslations[key], label: skipTranslation ? key : annotationTypesTranslations[key],
skipTranslation, skipTranslation,
color, metadata,
}); });
filterMap.set(key, filter); filterMap.set(key, filter);
filters.push(filter); filters.push(filter);

View File

@ -149,7 +149,12 @@ export class PdfProxyService {
private _configureElements() { private _configureElements() {
const dossierTemplateId = this._state.dossierTemplateId; const dossierTemplateId = this._state.dossierTemplateId;
const color = this._annotationDrawService.getAndConvertColor(dossierTemplateId, dossierTemplateId, 'manual'); const color = this._annotationDrawService.getAndConvertColor(
dossierTemplateId,
dossierTemplateId,
this._state.dossierDictionary,
'manual',
);
this._documentViewer.setRectangleToolStyles(color); this._documentViewer.setRectangleToolStyles(color);
} }

View File

@ -1,11 +1,11 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import { hexToRgb } from '../../../utils'; import { hexToRgb } from '@utils/functions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { UserPreferenceService } from '@services/user-preference.service'; import { UserPreferenceService } from '@services/user-preference.service';
import { RedactionLogService } from '@services/files/redaction-log.service'; import { RedactionLogService } from '@services/files/redaction-log.service';
import { IRectangle, ISectionGrid, ISectionRectangle } from '@red/domain'; import { Dictionary, IRectangle, ISectionGrid, ISectionRectangle } from '@red/domain';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { SuperTypes } from '@models/file/super-types'; import { SuperTypes } from '@models/file/super-types';
@ -32,16 +32,16 @@ export class AnnotationDrawService {
private readonly _documentViewer: REDDocumentViewer, private readonly _documentViewer: REDDocumentViewer,
) {} ) {}
async draw(annotations: List<AnnotationWrapper>, dossierTemplateId: string, hideSkipped: boolean) { async draw(annotations: List<AnnotationWrapper>, dossierTemplateId: string, hideSkipped: boolean, dossierDictionary: Dictionary) {
try { try {
await this._draw(annotations, dossierTemplateId, hideSkipped); await this._draw(annotations, dossierTemplateId, hideSkipped, dossierDictionary);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
} }
getAndConvertColor(superType: string, dossierTemplateId: string, dictionary?: string) { getAndConvertColor(superType: string, dossierTemplateId: string, dossierDictionary: Dictionary, type?: string) {
return this.convertColor(this.#getColor(superType, dossierTemplateId, dictionary)); return this.convertColor(this.#getColor(superType, dossierTemplateId, type, dossierDictionary));
} }
convertColor(hexColor: string) { convertColor(hexColor: string) {
@ -64,31 +64,45 @@ export class AnnotationDrawService {
return this._pdf.quad(x1, y1, x2, y2, x3, y3, x4, y4); return this._pdf.quad(x1, y1, x2, y2, x3, y3, x4, y4);
} }
#getColor(superType: string, dossierTemplateId: string, dictionary?: string) { #getColor(superType: string, dossierTemplateId: string, type: string, dossierDictionary: Dictionary) {
let color: string; let color: string;
let dictionary: Dictionary;
if (type === 'dossier_redaction') {
dictionary = dossierDictionary;
} else {
dictionary = this._dictionariesMapService.getDictionary(type, dossierTemplateId);
}
switch (superType) { switch (superType) {
case SuperTypes.Hint: case SuperTypes.Hint:
case SuperTypes.Redaction: case SuperTypes.Redaction:
color = this._dictionariesMapService.getDictionaryColor(dictionary, dossierTemplateId); color = dictionary.hexColor;
break; break;
case SuperTypes.Recommendation: case SuperTypes.Recommendation:
color = this._dictionariesMapService.getDictionaryColor(dictionary, dossierTemplateId, true); color = dictionary.recommendationHexColor;
break; break;
case SuperTypes.Skipped: case SuperTypes.Skipped:
case SuperTypes.IgnoredHint: case SuperTypes.IgnoredHint:
color = this._dictionariesMapService.getDictionaryColor(dictionary, dossierTemplateId, false, true); color = dictionary.skippedHexColor;
break; break;
default: default:
color = this._dictionariesMapService.getDictionaryColor(superType, dossierTemplateId); color = dictionary.hexColor;
break; break;
} }
return color; return color;
} }
private async _draw(annotationWrappers: List<AnnotationWrapper>, dossierTemplateId: string, hideSkipped: boolean) { private async _draw(
annotationWrappers: List<AnnotationWrapper>,
dossierTemplateId: string,
hideSkipped: boolean,
dossierDictionary: Dictionary,
) {
const totalPages = await firstValueFrom(this._pdf.totalPages$); const totalPages = await firstValueFrom(this._pdf.totalPages$);
const annotations = annotationWrappers const annotations = annotationWrappers
.map(annotation => this._computeAnnotation(annotation, dossierTemplateId, hideSkipped, totalPages)) .map(annotation => this._computeAnnotation(annotation, dossierTemplateId, hideSkipped, totalPages, dossierDictionary))
.filterTruthy(); .filterTruthy();
const documentLoaded = await firstValueFrom(this._documentViewer.loaded$); const documentLoaded = await firstValueFrom(this._documentViewer.loaded$);
if (!documentLoaded) { if (!documentLoaded) {
@ -100,22 +114,27 @@ export class AnnotationDrawService {
const { dossierId, fileId } = this._pdf; const { dossierId, fileId } = this._pdf;
const sectionsGrid$ = this._redactionLogService.getSectionGrid(dossierId, fileId); const sectionsGrid$ = this._redactionLogService.getSectionGrid(dossierId, fileId);
const sectionsGrid = await firstValueFrom(sectionsGrid$).catch(() => ({ rectanglesPerPage: {} })); const sectionsGrid = await firstValueFrom(sectionsGrid$).catch(() => ({ rectanglesPerPage: {} }));
await this._drawSections(sectionsGrid, dossierTemplateId); await this._drawSections(sectionsGrid, dossierTemplateId, dossierDictionary);
} }
} }
private async _drawSections(sectionGrid: ISectionGrid, dossierTemplateId: string) { private async _drawSections(sectionGrid: ISectionGrid, dossierTemplateId: string, dossierDictionary: Dictionary) {
const sections: Core.Annotations.RectangleAnnotation[] = []; const sections: Core.Annotations.RectangleAnnotation[] = [];
for (const page of Object.keys(sectionGrid.rectanglesPerPage)) { for (const page of Object.keys(sectionGrid.rectanglesPerPage)) {
const sectionRectangles = sectionGrid.rectanglesPerPage[page]; const sectionRectangles = sectionGrid.rectanglesPerPage[page];
sectionRectangles.forEach(sectionRectangle => { sectionRectangles.forEach(sectionRectangle => {
sections.push(this._computeSection(dossierTemplateId, parseInt(page, 10), sectionRectangle)); sections.push(this._computeSection(dossierTemplateId, parseInt(page, 10), sectionRectangle, dossierDictionary));
}); });
} }
await this._annotationManager.add(sections); await this._annotationManager.add(sections);
} }
private _computeSection(dossierTemplateId: string, pageNumber: number, sectionRectangle: ISectionRectangle) { private _computeSection(
dossierTemplateId: string,
pageNumber: number,
sectionRectangle: ISectionRectangle,
dossierDictionary: Dictionary,
) {
const rectangleAnnot = this._pdf.rectangle(); const rectangleAnnot = this._pdf.rectangle();
const pageHeight = this._documentViewer.getHeight(pageNumber); const pageHeight = this._documentViewer.getHeight(pageNumber);
const rectangle: IRectangle = { const rectangle: IRectangle = {
@ -130,13 +149,19 @@ export class AnnotationDrawService {
rectangleAnnot.Width = rectangle.width + 2; rectangleAnnot.Width = rectangle.width + 2;
rectangleAnnot.Height = rectangle.height + 2; rectangleAnnot.Height = rectangle.height + 2;
rectangleAnnot.ReadOnly = true; rectangleAnnot.ReadOnly = true;
rectangleAnnot.StrokeColor = this.getAndConvertColor('analysis', dossierTemplateId, 'analysis'); rectangleAnnot.StrokeColor = this.getAndConvertColor('analysis', dossierTemplateId, dossierDictionary, 'analysis');
rectangleAnnot.StrokeThickness = 1; rectangleAnnot.StrokeThickness = 1;
return rectangleAnnot; return rectangleAnnot;
} }
private _computeAnnotation(annotationWrapper: AnnotationWrapper, dossierTemplateId: string, hideSkipped: boolean, totalPages: number) { private _computeAnnotation(
annotationWrapper: AnnotationWrapper,
dossierTemplateId: string,
hideSkipped: boolean,
totalPages: number,
dossierDictionary: Dictionary,
) {
const pageNumber = this._pdf.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber; const pageNumber = this._pdf.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
if (pageNumber > totalPages) { if (pageNumber > totalPages) {
// skip imported annotations from files that have more pages than the current one // skip imported annotations from files that have more pages than the current one
@ -165,7 +190,12 @@ export class AnnotationDrawService {
annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY; annotation.Opacity = annotationWrapper.isChangeLogRemoved ? DEFAULT_REMOVED_ANNOTATION_OPACITY : DEFAULT_TEXT_ANNOTATION_OPACITY;
annotation.setContents(annotationWrapper.content); annotation.setContents(annotationWrapper.content);
annotation.PageNumber = pageNumber; annotation.PageNumber = pageNumber;
annotation.StrokeColor = this.getAndConvertColor(annotationWrapper.superType, dossierTemplateId, annotationWrapper.type); annotation.StrokeColor = this.getAndConvertColor(
annotationWrapper.superType,
dossierTemplateId,
dossierDictionary,
annotationWrapper.type,
);
annotation.Id = annotationWrapper.id; annotation.Id = annotationWrapper.id;
annotation.ReadOnly = true; annotation.ReadOnly = true;
@ -180,10 +210,10 @@ export class AnnotationDrawService {
annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry)); annotation.setCustomData('changeLog', String(annotationWrapper.isChangeLogEntry));
annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved)); annotation.setCustomData('changeLogRemoved', String(annotationWrapper.isChangeLogRemoved));
annotation.setCustomData('opacity', String(annotation.Opacity)); annotation.setCustomData('opacity', String(annotation.Opacity));
annotation.setCustomData('redactionColor', String(this.#getColor('redaction', dossierTemplateId, 'preview'))); annotation.setCustomData('redactionColor', String(this.#getColor('redaction', dossierTemplateId, 'preview', dossierDictionary)));
annotation.setCustomData( annotation.setCustomData(
'annotationColor', 'annotationColor',
String(this.#getColor(annotationWrapper.superType, dossierTemplateId, annotationWrapper.type)), String(this.#getColor(annotationWrapper.superType, dossierTemplateId, annotationWrapper.type, dossierDictionary)),
); );
return annotation; return annotation;

View File

@ -1,10 +1,3 @@
<div <div #icon [class]="'icon ' + type">
#icon <span>{{ label }}</span>
[class.hint]="isHint"
[class.none]="type === 'none'"
[class.recommendation]="isRecommendation"
[class.request]="isRequest"
class="icon"
>
<span>{{ label || dictionary?.label?.charAt(0) }}</span>
</div> </div>

View File

@ -14,7 +14,7 @@
background-color: var(--color); background-color: var(--color);
} }
.request { .rhombus {
transform: scale(0.8) rotate(45deg); transform: scale(0.8) rotate(45deg);
span { span {
@ -23,7 +23,7 @@
} }
} }
.recommendation { .hexagon {
position: relative; position: relative;
width: 16px; width: 16px;
height: 10px; height: 10px;
@ -52,15 +52,10 @@
} }
} }
.hint, .circle {
.skipped {
border-radius: 50%; border-radius: 50%;
} }
.skipped {
background-color: var(--iqser-grey-5);
}
.none { .none {
color: var(--iqser-accent); color: var(--iqser-accent);
} }

View File

@ -1,36 +1,19 @@
import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core'; import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core';
import { Dictionary } from '@red/domain'; import { AnnotationIconType } from '@red/domain';
@Component({ @Component({
selector: 'redaction-annotation-icon', selector: 'redaction-annotation-icon [color] [type] [label]',
templateUrl: './annotation-icon.component.html', templateUrl: './annotation-icon.component.html',
styleUrls: ['./annotation-icon.component.scss'], styleUrls: ['./annotation-icon.component.scss'],
}) })
export class AnnotationIconComponent implements OnChanges { export class AnnotationIconComponent implements OnChanges {
@Input() color: string; @Input() color: string;
@Input() type: 'square' | 'rhombus' | 'circle' | 'hexagon' | 'none'; @Input() type: AnnotationIconType;
@Input() label: string; @Input() label: string;
@Input() dictionary: Dictionary;
@ViewChild('icon', { static: true }) icon: ElementRef; @ViewChild('icon', { static: true }) icon: ElementRef;
get isHint() {
return this.type === 'circle' || this.dictionary?.type === 'hint';
}
get isRequest() {
return this.type === 'rhombus' || this.dictionary?.type === 'redaction';
}
get isRecommendation() {
return this.type === 'hexagon' || this.dictionary?.type === 'recommendation';
}
get backgroundColor() {
return this.color || this.dictionary?.hexColor;
}
ngOnChanges() { ngOnChanges() {
this.icon.nativeElement.style.setProperty('--color', this.backgroundColor); this.icon.nativeElement.style.setProperty('--color', this.color);
} }
} }

View File

@ -1 +0,0 @@
<redaction-annotation-icon [color]="color" [label]="label" [type]="type"></redaction-annotation-icon>

View File

@ -1,27 +0,0 @@
import { Component, Input, OnChanges } from '@angular/core';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
@Component({
selector: 'redaction-dictionary-annotation-icon',
templateUrl: './dictionary-annotation-icon.component.html',
styleUrls: ['./dictionary-annotation-icon.component.scss'],
})
export class DictionaryAnnotationIconComponent implements OnChanges {
@Input() dictionaryKey: string;
@Input() dossierTemplateId: string;
color: string;
label: string;
type: 'square' | 'circle';
constructor(private readonly _dictionariesMapService: DictionariesMapService) {}
ngOnChanges(): void {
if (this.dictionaryKey) {
const dictionary = this._dictionariesMapService.getDictionary(this.dictionaryKey, this.dossierTemplateId);
this.color = this._dictionariesMapService.getDictionaryColor(this.dictionaryKey, this.dossierTemplateId);
this.type = dictionary.hint ? 'circle' : 'square';
this.label = this.dictionaryKey[0].toUpperCase();
}
}
}

View File

@ -1,67 +1,14 @@
<ng-container *ngIf="!filter.icon"> <ng-container *ngIf="!filter.icon">
<redaction-annotation-icon
*ngIf="filter.id === 'redaction'"
[color]="dictionaryColor"
label="R"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="filter.id === 'recommendation'"
[color]="dictionaryColor"
label="R"
type="hexagon"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.id === 'hint'" [color]="dictionaryColor" label="H" type="circle"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="filter.id === 'manual-redaction'"
[color]="dictionaryColor"
label="M"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="filter.id === 'skipped'"
[color]="dictionaryColor"
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"
label="S"
type="rhombus"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="needsAnalysis(filter.id)"
[color]="dictionaryColor"
label="A"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="filter.id === 'declined-suggestion'"
[color]="dictionaryColor"
label="S"
type="rhombus"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.id === 'none'" color="transparent" label="-" type="none"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="filter.id === 'updated'"
[color]="dictionaryColor"
label="U"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="filter.id === 'image'" [color]="dictionaryColor" label="I" type="square"></redaction-annotation-icon>
<div *ngIf="filter.id === 'comment'"> <div *ngIf="filter.id === 'comment'">
<mat-icon svgIcon="red:comment"></mat-icon> <mat-icon svgIcon="red:comment"></mat-icon>
</div> </div>
<redaction-annotation-icon *ngIf="filter.color" [color]="filter.color" [label]="''" type="square"></redaction-annotation-icon> <redaction-annotation-icon
*ngIf="filter.id !== 'comment'"
[color]="color"
[label]="label"
[type]="filter.metadata?.shape"
></redaction-annotation-icon>
</ng-container> </ng-container>
<ng-container *ngIf="filter.icon"> <ng-container *ngIf="filter.icon">

View File

@ -1,35 +1,47 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { INestedFilter, Required } from '@iqser/common-ui'; import { INestedFilter } from '@iqser/common-ui';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { SuperTypes } from '@models/file/super-types';
@Component({ @Component({
selector: 'redaction-type-filter', selector: 'redaction-type-filter [dossierTemplateId]',
templateUrl: './type-filter.component.html', templateUrl: './type-filter.component.html',
styleUrls: ['./type-filter.component.scss'], styleUrls: ['./type-filter.component.scss'],
}) })
export class TypeFilterComponent implements OnChanges { export class TypeFilterComponent implements OnChanges {
@Input() filter: INestedFilter; @Input() filter: INestedFilter;
@Input() @Required() dossierTemplateId!: string; @Input() dossierTemplateId: string;
@Input() dossierId: string;
dictionaryColor: string; dictionaryColor: string;
label: string;
color: string;
private _suggestionsKeys = [ private _suggestionsKeys: string[] = [
'suggestion-remove', SuperTypes.SuggestionRemove,
SuperTypes.SuggestionForceRedaction,
SuperTypes.SuggestionAdd,
SuperTypes.SuggestionRemoveDictionary,
SuperTypes.SuggestionAddDictionary,
SuperTypes.DeclinedSuggestion,
'suggestion', 'suggestion',
'suggestion-force-redaction',
'suggestion-add',
'suggestion-remove-dictionary',
'suggestion-add-dictionary',
]; ];
private _needsAnalysisKeys = ['add-dictionary', 'remove-only-here', 'change-legal-basis', 'analysis']; private _needsAnalysisKeys: string[] = ['remove-only-here', 'analysis'];
constructor(private readonly _dictionariesMapService: DictionariesMapService) {} constructor(private readonly _dictionariesMapService: DictionariesMapService) {}
isSuggestion = (key: string) => this._suggestionsKeys.includes(key);
needsAnalysis = (key: string) => this._needsAnalysisKeys.includes(key);
ngOnChanges(): void { ngOnChanges(): void {
this.dictionaryColor = this._dictionariesMapService.getDictionaryColor(this.filter.id, this.dossierTemplateId); this.dictionaryColor = this._dictionariesMapService.getDictionaryColor(this.filter.id, this.dossierTemplateId);
this.label =
this.filter.metadata?.shortLabel === ''
? ''
: this.filter.id === 'none'
? '-'
: this._needsAnalysisKeys.includes(this.filter.id)
? 'A'
: this._suggestionsKeys.includes(this.filter.id)
? 'S'
: this.filter.id.charAt(0);
this.color = this.filter.metadata?.color || (this.filter.id === 'none' ? 'transparent' : this.dictionaryColor);
} }
} }

View File

@ -10,7 +10,6 @@ import { IconsModule } from '../icons/icons.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component'; import { AnnotationIconComponent } from './components/annotation-icon/annotation-icon.component';
import { DonutChartComponent } from './components/donut-chart/donut-chart.component'; import { DonutChartComponent } from './components/donut-chart/donut-chart.component';
import { DictionaryAnnotationIconComponent } from './components/dictionary-annotation-icon/dictionary-annotation-icon.component';
import { CommonUiModule } from '@iqser/common-ui'; import { CommonUiModule } from '@iqser/common-ui';
import { SelectComponent } from './components/select/select.component'; import { SelectComponent } from './components/select/select.component';
import { NavigateLastDossiersScreenDirective } from './directives/navigate-last-dossiers-screen.directive'; import { NavigateLastDossiersScreenDirective } from './directives/navigate-last-dossiers-screen.directive';
@ -46,7 +45,6 @@ const components = [
PaginationComponent, PaginationComponent,
AnnotationIconComponent, AnnotationIconComponent,
DonutChartComponent, DonutChartComponent,
DictionaryAnnotationIconComponent,
SelectComponent, SelectComponent,
DictionaryManagerComponent, DictionaryManagerComponent,
AssignUserDropdownComponent, AssignUserDropdownComponent,

View File

@ -13,6 +13,7 @@ export class DictionariesMapService extends EntitiesMapService<Dictionary, IDict
return this.get(dossierTemplateId, type) || this.get(dossierTemplateId, 'default'); return this.get(dossierTemplateId, type) || this.get(dossierTemplateId, 'default');
} }
// todo is this necessary?
getDictionaryColor(type: string, dossierTemplateId: string, isRecommendation = false, isSkipped = false) { getDictionaryColor(type: string, dossierTemplateId: string, isRecommendation = false, isSkipped = false) {
const defaultColor = '#CCCCCC'; const defaultColor = '#CCCCCC';
if (!this.get(dossierTemplateId)) { if (!this.get(dossierTemplateId)) {

View File

@ -5,7 +5,7 @@ export class GlobalErrorHandler extends ErrorHandler {
handleError(error: Error): void { handleError(error: Error): void {
const chunkFailedMessage = /Loading chunk [\d]+ failed/; const chunkFailedMessage = /Loading chunk [\d]+ failed/;
console.write(error); console.write(error);
if (error.message.includes('An error happened during access validation')) { if (error.message?.includes('An error happened during access validation')) {
console.log('User is not authorized to access this page'); console.log('User is not authorized to access this page');
} }

View File

@ -16,3 +16,23 @@ export type ManualRedactionActions =
| 'undo' | 'undo'
| 'force-redaction' | 'force-redaction'
| 'request-force-redaction'; | 'request-force-redaction';
export const AnnotationIconTypes = {
square: 'square',
rhombus: 'rhombus',
circle: 'circle',
hexagon: 'hexagon',
none: 'none',
} as const;
export type AnnotationIconType = keyof typeof AnnotationIconTypes;
export const AnnotationShapeMap: Record<string, AnnotationIconType> = {
analysis: AnnotationIconTypes.square,
hint: AnnotationIconTypes.circle,
redaction: AnnotationIconTypes.square,
suggestion: AnnotationIconTypes.rhombus,
updated: AnnotationIconTypes.square,
image: AnnotationIconTypes.square,
none: AnnotationIconTypes.none,
};