RED-4639: Refactor colors - refresh after changes

This commit is contained in:
Adina Țeudan 2022-07-18 20:38:25 +03:00
parent 408d40ad16
commit 5f883941cc
19 changed files with 136 additions and 71 deletions

View File

@ -5,6 +5,7 @@ import { FilesService } from '@services/files/files.service';
import { firstValueFrom } from 'rxjs';
import { DOSSIER_ID, DOSSIER_TEMPLATE_ID } from '@red/domain';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
@Injectable({ providedIn: 'root' })
export class DossierFilesGuard implements CanActivate {
@ -12,6 +13,7 @@ export class DossierFilesGuard implements CanActivate {
private readonly _injector: Injector,
private readonly _filesMapService: FilesMapService,
private readonly _filesService: FilesService,
private readonly _dictionaryService: DictionaryService,
private readonly _router: Router,
) {}
@ -21,6 +23,8 @@ export class DossierFilesGuard implements CanActivate {
const token: ProviderToken<DossiersService> = route.data.dossiersService;
const dossiersService: DossiersService = this._injector.get<DossiersService>(token);
await firstValueFrom(this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId));
if (!dossiersService.has(dossierId)) {
await this._router.navigate(['/main', dossierTemplateId]);
return false;

View File

@ -1,14 +1,29 @@
<div class="needs-work">
<redaction-annotation-icon *ngIf="file.analysisRequired" [color]="analysisColor" label="A" type="square"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.analysisRequired"
[color]="analysisColor$ | async"
label="A"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hasUpdates && file.assignee === userService.currentUser.id && !file.isApproved"
[color]="updatedColor"
[color]="updatedColor$ | async"
label="U"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hasRedactions" [color]="redactionColor" label="R" type="square"></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hasImages" [color]="imageColor" label="I" type="square"></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hintsOnly" [color]="hintColor" label="H" type="circle"></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hasSuggestions" [color]="suggestionColor" label="S" type="rhombus"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hasRedactions"
[color]="redactionColor$ | async"
label="R"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hasImages" [color]="imageColor$ | async" label="I" type="square"></redaction-annotation-icon>
<redaction-annotation-icon *ngIf="file.hintsOnly" [color]="hintColor$ | async" label="H" type="circle"></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="file.hasSuggestions"
[color]="suggestionColor$ | async"
label="S"
type="rhombus"
></redaction-annotation-icon>
<mat-icon *ngIf="file.hasAnnotationComments" svgIcon="red:comment"></mat-icon>
</div>

View File

@ -1,8 +1,9 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import { annotationDefaultColorConfig, DefaultBasedColorType, File } from '@red/domain';
import { UserService } from '@services/user.service';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { Observable } from 'rxjs';
@Component({
selector: 'redaction-file-workload',
@ -10,41 +11,35 @@ import { DefaultColorsService } from '@services/entity-services/default-colors.s
styleUrls: ['./file-workload.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FileWorkloadComponent {
export class FileWorkloadComponent implements OnInit {
@Input() file: File;
suggestionColor$: Observable<string>;
imageColor$: Observable<string>;
updatedColor$: Observable<string>;
analysisColor$: Observable<string>;
hintColor$: Observable<string>;
redactionColor$: Observable<string>;
#dossierTemplateId: string;
constructor(
readonly userService: UserService,
private readonly _defaultColorsService: DefaultColorsService,
private readonly _dossiersService: DossiersService,
) {}
get suggestionColor() {
return this._getDefaultColor('suggestion');
ngOnInit() {
this.#dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
this.suggestionColor$ = this.#getDefaultColor$('suggestion');
this.imageColor$ = this.#getDefaultColor$('recommendation');
this.updatedColor$ = this.#getDefaultColor$('updated');
this.analysisColor$ = this.#getDefaultColor$('analysis');
this.hintColor$ = this.#getDefaultColor$('hint');
this.redactionColor$ = this.#getDefaultColor$('redaction');
}
get imageColor() {
return this._getDefaultColor('recommendation');
}
get updatedColor() {
return this._getDefaultColor('updated');
}
get analysisColor() {
return this._getDefaultColor('analysis');
}
get hintColor() {
return this._getDefaultColor('hint');
}
get redactionColor() {
return this._getDefaultColor('redaction');
}
private _getDefaultColor(type: DefaultBasedColorType) {
const dossierTemplateId = this._dossiersService.find(this.file.dossierId).dossierTemplateId;
return this._defaultColorsService.getColor(dossierTemplateId, annotationDefaultColorConfig[type]);
#getDefaultColor$(type: DefaultBasedColorType): Observable<string> {
return this._defaultColorsService.getColor$(this.#dossierTemplateId, annotationDefaultColorConfig[type]);
}
}

View File

@ -289,8 +289,8 @@ export class ConfigService {
id: item,
label: workloadTranslations[item],
metadata: {
color: this._defaultColorsService.getColor(dossierTemplateId, annotationDefaultColorConfig[item]),
shape: AnnotationShapeMap[item],
color$: this._defaultColorsService.getColor$(dossierTemplateId, annotationDefaultColorConfig[item]),
},
}),
);

View File

@ -1,21 +1,21 @@
<div class="needs-work">
<redaction-annotation-icon
*ngIf="dossierStats.hasRedactionsFilePresent"
[color]="redactionColor"
[color]="redactionColor$ | async"
label="R"
type="square"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="dossierStats.hasHintsNoRedactionsFilePresent"
[color]="hintColor"
[color]="hintColor$ | async"
label="H"
type="circle"
></redaction-annotation-icon>
<redaction-annotation-icon
*ngIf="dossierStats.hasSuggestionsFilePresent"
[color]="suggestionColor"
[color]="suggestionColor$ | async"
label="S"
type="rhombus"
></redaction-annotation-icon>

View File

@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { Dossier, DossierStats } from '@red/domain';
import { ChangeDetectionStrategy, Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { DefaultColorType, Dossier, DossierStats } from '@red/domain';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { BehaviorSubject, Observable, switchMap } from 'rxjs';
@Component({
selector: 'redaction-dossier-workload-column',
@ -8,21 +9,33 @@ import { DefaultColorsService } from '@services/entity-services/default-colors.s
styleUrls: ['./dossier-workload-column.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DossierWorkloadColumnComponent {
export class DossierWorkloadColumnComponent implements OnChanges {
@Input() dossier: Dossier;
@Input() dossierStats: DossierStats;
constructor(private readonly _colorsService: DefaultColorsService) {}
readonly suggestionColor$: Observable<string>;
readonly hintColor$: Observable<string>;
readonly redactionColor$: Observable<string>;
get suggestionColor() {
return this._colorsService.getColor(this.dossier.dossierTemplateId, 'requestAddColor');
readonly #dossierTemplateId$ = new BehaviorSubject<string>(null);
constructor(private readonly _defaultColorsService: DefaultColorsService) {
this.suggestionColor$ = this.#dossierTemplateId$.pipe(
switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'requestAddColor')),
);
this.hintColor$ = this.#dossierTemplateId$.pipe(switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'hintColor')));
this.redactionColor$ = this.#dossierTemplateId$.pipe(
switchMap(dossierTemplateId => this.#getColor$(dossierTemplateId, 'redactionColor')),
);
}
get hintColor() {
return this._colorsService.getColor(this.dossier.dossierTemplateId, 'hintColor');
ngOnChanges(changes: SimpleChanges): void {
if (changes.dossier) {
this.#dossierTemplateId$.next(this.dossier.dossierTemplateId);
}
}
get redactionColor() {
return this._colorsService.getColor(this.dossier.dossierTemplateId, 'redactionColor');
#getColor$(dossierTemplateId: string, colorType: DefaultColorType): Observable<string> {
return this._defaultColorsService.getColor$(dossierTemplateId, colorType);
}
}

View File

@ -1,6 +1,14 @@
import { Injectable, TemplateRef } from '@angular/core';
import { ButtonConfig, IFilterGroup, INestedFilter, keyChecker, NestedFilter, TableColumnConfig } from '@iqser/common-ui';
import { AnnotationShapeMap, Dossier, DossierTemplate, StatusSorter, User, WorkflowFileStatus } from '@red/domain';
import {
annotationDefaultColorConfig,
AnnotationShapeMap,
Dossier,
DossierTemplate,
StatusSorter,
User,
WorkflowFileStatus,
} from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { TranslateService } from '@ngx-translate/core';
import { UserPreferenceService } from '@services/user-preference.service';
@ -12,6 +20,7 @@ import { DossierStatsService } from '@services/dossiers/dossier-stats.service';
import { DossierStatesMapService } from '@services/entity-services/dossier-states-map.service';
import { PermissionsService } from '@services/permissions.service';
import { SharedDialogService } from '@shared/services/dialog.service';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
@Injectable()
export class ConfigService {
@ -23,6 +32,7 @@ export class ConfigService {
private readonly _dossierStatesMapService: DossierStatesMapService,
private readonly _dialogService: SharedDialogService,
private readonly _permissionsService: PermissionsService,
private readonly _defaultColorsService: DefaultColorsService,
) {}
get tableConfig(): TableColumnConfig<Dossier>[] {
@ -53,7 +63,7 @@ export class ConfigService {
];
}
filterGroups(entities: Dossier[], needsWorkFilterTemplate: TemplateRef<unknown>) {
filterGroups(entities: Dossier[], needsWorkFilterTemplate: TemplateRef<unknown>, dossierTemplateId: string) {
const allDistinctFileStatus = new Set<string>();
const allDistinctPeople = new Set<string>();
const allDistinctNeedsWork = new Set<string>();
@ -145,7 +155,10 @@ export class ConfigService {
new NestedFilter({
id: type,
label: workloadTranslations[type],
metadata: { shape: AnnotationShapeMap[type] },
metadata: {
shape: AnnotationShapeMap[type],
color$: this._defaultColorsService.getColor$(dossierTemplateId, annotationDefaultColorConfig[type]),
},
}),
);

View File

@ -62,7 +62,11 @@ export class DossiersListingScreenComponent extends ListingComponent<Dossier> im
}
private _computeAllFilters() {
const filterGroups = this._configService.filterGroups(this.entitiesService.all, this._needsWorkFilterTemplate);
const filterGroups = this._configService.filterGroups(
this.entitiesService.all,
this._needsWorkFilterTemplate,
this.dossierTemplate.id,
);
this.filterService.addFilterGroups(filterGroups);
}
}

View File

@ -8,6 +8,7 @@ import { annotationDefaultColorConfig, IViewedPage } from '@red/domain';
import { FilePreviewStateService } from './file-preview-state.service';
import { FileDataService } from './file-data.service';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { of } from 'rxjs';
@Injectable()
export class AnnotationProcessingService {
@ -68,7 +69,7 @@ export class AnnotationProcessingService {
// top level filter
if (topLevelFilter) {
this._createParentFilter(a.isHighlight ? a.filterKey : a.superType, filterMap, filters, a.isHighlight, {
color: a.color,
color$: of(a.color),
shortLabel: a.isHighlight ? '' : null,
shape: a.iconShape,
});
@ -78,7 +79,7 @@ export class AnnotationProcessingService {
if (!parentFilter) {
parentFilter = this._createParentFilter(a.superType, filterMap, filters, false, {
shape: a.iconShape,
color: this._defaultColorsService.getColor(
color$: this._defaultColorsService.getColor$(
this._state.dossierTemplateId,
annotationDefaultColorConfig[a.superType],
),
@ -90,7 +91,7 @@ export class AnnotationProcessingService {
checked: false,
matches: 1,
metadata: {
color: a.color,
color$: of(a.color),
shape: a.iconShape,
},
skipTranslation: true,

View File

@ -19,6 +19,7 @@ import { NGXLogger } from 'ngx-logger';
import { MultiSelectService } from './multi-select.service';
import { FilesService } from '@services/files/files.service';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
@ -38,6 +39,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
private readonly _viewModeService: ViewModeService,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _dossierDictionariesMapService: DossierDictionariesMapService,
private readonly _permissionsService: PermissionsService,
private readonly _redactionLogService: RedactionLogService,
private readonly _textHighlightsService: TextHighlightService,
@ -69,7 +71,11 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
get #annotations$() {
return this.#redactionLog$.pipe(
withLatestFrom(this._state.file$),
withLatestFrom(
this._state.file$,
this._dictionariesMapService.get$(this._state.dossierTemplateId),
this._dossierDictionariesMapService.get$(this._state.dossierId),
),
map(([redactionLog, file]) => this.#buildAnnotations(redactionLog, file)),
tap(() => this.#checkMissingTypes()),
map(annotations =>

View File

@ -11,11 +11,11 @@ import { dossiersServiceResolver } from '@services/entity-services/dossiers.serv
import { wipeFilesCache } from '@iqser/cache';
import { DossiersService } from '@services/dossiers/dossiers.service';
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';
import { MatDialogRef } from '@angular/material/dialog';
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
const ONE_MEGABYTE = 1024 * 1024;
@ -56,7 +56,7 @@ export class FilePreviewStateService {
private readonly _filesService: FilesService,
private readonly _dossiersService: DossiersService,
private readonly _fileManagementService: FileManagementService,
private readonly _dictionaryService: DictionaryService,
private readonly _dossierDictionariesMapService: DossierDictionariesMapService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _translateService: TranslateService,
private readonly _loadingService: LoadingService,
@ -75,8 +75,8 @@ export class FilePreviewStateService {
this.blob$ = this.#blob$;
this.dossierFileChange$ = this.#dossierFilesChange$();
this._dictionaryService
.getDossierDictionary(this.dossierTemplateId, this.dossierId)
this._dossierDictionariesMapService
.watch$(this.dossierId, 'dossier_redaction')
.subscribe(dictionary => (this.#dossierDictionary = dictionary));
}

View File

@ -83,6 +83,6 @@ export class EditDossierDictionaryComponent implements EditDossierSectionInterfa
private async _updateDossierDictionary() {
const { dossierId, dossierTemplateId } = this.dossier;
this.dossierDictionary = await firstValueFrom(this._dictionaryService.getDossierDictionary(dossierTemplateId, dossierId));
this.dossierDictionary = await firstValueFrom(this._dictionaryService.loadDossierDictionary(dossierTemplateId, dossierId));
}
}

View File

@ -5,9 +5,9 @@
<redaction-annotation-icon
*ngIf="filter.id !== 'comment'"
[color]="color"
[color]="filter.metadata.color$ | async"
[label]="label"
[type]="filter.metadata?.shape"
[type]="filter.metadata.shape"
></redaction-annotation-icon>
</ng-container>

View File

@ -14,7 +14,6 @@ export class TypeFilterComponent implements OnChanges {
@Input() dossierId: string;
label: string;
color: string;
private _suggestionsKeys: string[] = [
SuperTypes.SuggestionRemove,
@ -40,7 +39,5 @@ export class TypeFilterComponent implements OnChanges {
: this._suggestionsKeys.includes(this.filter.id)
? 'S'
: this.filter.id.charAt(0);
this.color = this.filter.metadata?.color || 'transparent';
}
}

View File

@ -15,6 +15,13 @@ export class DefaultColorsService extends EntitiesService<IDefaultColors, Defaul
return this.find(dossierTemplateId)[colorType];
}
getColor$(dossierTemplateId: string, colorType: DefaultColorType, fallback = 'transparent'): Observable<string> {
return this.getEntityChanged$(dossierTemplateId).pipe(
map(c => c[colorType]),
map(c => c || fallback),
);
}
loadAll(dossierTemplateIds: string[]): Observable<DefaultColors[]> {
return forkJoin(dossierTemplateIds.map(id => super.getFor<IDefaultColors>(id))).pipe(
mapEach(defaultColors => new DefaultColors(defaultColors)),

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { firstValueFrom, forkJoin, Observable, throwError } from 'rxjs';
import { forkJoin, Observable, throwError } from 'rxjs';
import { EntitiesService, List, QueryParam, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { Dictionary, DictionaryEntryType, DictionaryEntryTypes, IDictionary, IUpdateDictionary, SuperTypes } from '@red/domain';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
@ -8,6 +8,7 @@ import { DossierTemplateStatsService } from './dossier-template-stats.service';
import { DictionariesMapService } from './dictionaries-map.service';
import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http';
import { FALLBACK_COLOR } from '@utils/constants';
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
const MIN_WORD_LENGTH = 2;
@ -22,6 +23,7 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
private readonly _toaster: Toaster,
private readonly _dossierTemplateStatsService: DossierTemplateStatsService,
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _dossierDictionariesMapService: DossierDictionariesMapService,
) {
super();
}
@ -144,7 +146,7 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
async getDictionariesOptions(dossierTemplateId: string, dossierId: string): Promise<Dictionary[]> {
const possibleDictionaries: Dictionary[] = [];
const dossierDictionary: Dictionary = await firstValueFrom(this.getDossierDictionary(dossierTemplateId, dossierId));
const dossierDictionary: Dictionary = this._dossierDictionariesMapService.get(dossierId, 'dossier_redaction');
for (const dictionary of this._dictionariesMapService.get(dossierTemplateId)) {
if (!dictionary.virtual && dictionary.addToDictionaryAction) {
@ -161,7 +163,7 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
return possibleDictionaries;
}
getDossierDictionary(dossierTemplateId: string, dossierId: string): Observable<Dictionary> {
loadDossierDictionary(dossierTemplateId: string, dossierId: string): Observable<Dictionary> {
return this.getForType(dossierTemplateId, 'dossier_redaction', dossierId).pipe(
map(
dictionary =>
@ -170,6 +172,9 @@ export class DictionaryService extends EntitiesService<IDictionary, Dictionary>
type: 'dossier_redaction',
}),
),
tap(dictionary => {
this._dossierDictionariesMapService.set(dossierId, [dictionary]);
}),
);
}

View File

@ -0,0 +1,8 @@
import { Injectable } from '@angular/core';
import { Dictionary, DOSSIER_ID, IDictionary } from '@red/domain';
import { EntitiesMapService } from '@iqser/common-ui';
@Injectable({ providedIn: 'root' })
export class DossierDictionariesMapService extends EntitiesMapService<Dictionary, IDictionary> {
protected readonly _primaryKey = DOSSIER_ID;
}

View File

@ -15,7 +15,6 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
readonly rank?: number;
readonly recommendation: boolean;
readonly type: string;
readonly typeId?: string;
readonly hasDictionary?: boolean;
readonly systemManaged?: boolean;
@ -40,7 +39,6 @@ export class Dictionary extends Entity<IDictionary> implements IDictionary {
this.rank = entity.rank;
this.recommendation = !!entity.recommendation;
this.type = entity.type;
this.typeId = entity.typeId;
this.hasDictionary = entity.hasDictionary;
this.systemManaged = entity.systemManaged;
}

View File

@ -24,7 +24,6 @@ export interface IDictionary {
* The nonnull entry type.
*/
readonly type: string;
readonly typeId?: string;
/**
* The list of dictionary entries of an entry type.
*/