-
diff --git a/apps/red-ui/src/app/modules/admin/shared/components/add-edit-entity/add-edit-entity.component.ts b/apps/red-ui/src/app/modules/admin/shared/components/add-edit-entity/add-edit-entity.component.ts
index 88e8bdd58..8f1e3277c 100644
--- a/apps/red-ui/src/app/modules/admin/shared/components/add-edit-entity/add-edit-entity.component.ts
+++ b/apps/red-ui/src/app/modules/admin/shared/components/add-edit-entity/add-edit-entity.component.ts
@@ -1,6 +1,6 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { Dictionary, IDictionary } from '@red/domain';
-import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
+import { FormGroup, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { map, startWith } from 'rxjs/operators';
import { firstValueFrom, Observable } from 'rxjs';
import { toSnakeCase } from '@utils/functions';
@@ -10,7 +10,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { BaseFormComponent, LoadingService, Toaster } from '@iqser/common-ui';
-const REDACTION_FIELDS = ['defaultReason', 'addToDictionaryAction'];
+const REDACTION_FIELDS = ['defaultReason'];
@Component({
selector: 'redaction-add-edit-entity [entity] [dossierTemplateId]',
@@ -22,10 +22,10 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
@Input() dossierTemplateId: string;
@Input() entity: Dictionary;
- hasHexColor$: Observable
;
- hasRecommendationHexColor$: Observable;
technicalName$: Observable;
+ colors: { label: string; placeholder: string; controlName: string; hasColor$: Observable }[];
+
constructor(
private readonly _dictionariesMapService: DictionariesMapService,
private readonly _permissionsService: PermissionsService,
@@ -77,6 +77,29 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
}
}
+ #initializeColors(form: FormGroup): void {
+ this.colors = [
+ {
+ label: _('add-edit-entity.form.color'),
+ placeholder: _('add-edit-entity.form.color-placeholder'),
+ controlName: 'hexColor',
+ hasColor$: this._colorEmpty$(form, 'hexColor'),
+ },
+ {
+ label: _('add-edit-entity.form.recommendation-color'),
+ placeholder: _('add-edit-entity.form.recommendation-color-placeholder'),
+ controlName: 'recommendationHexColor',
+ hasColor$: this._colorEmpty$(form, 'recommendationHexColor'),
+ },
+ {
+ label: _('add-edit-entity.form.skipped-color'),
+ placeholder: _('add-edit-entity.form.skipped-color-placeholder'),
+ controlName: 'skippedHexColor',
+ hasColor$: this._colorEmpty$(form, 'skippedHexColor'),
+ },
+ ];
+ }
+
private _initializeForm(): void {
const controlsConfig = {
type: [this.entity?.type],
@@ -85,6 +108,7 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
rank: [{ value: this.entity?.rank, disabled: this.#isSystemManaged }, Validators.required],
hexColor: [this.entity?.hexColor, [Validators.required, Validators.minLength(7)]],
recommendationHexColor: [this.entity?.recommendationHexColor, [Validators.required, Validators.minLength(7)]],
+ skippedHexColor: [this.entity?.skippedHexColor, [Validators.required, Validators.minLength(7)]],
hint: [{ value: !!this.entity?.hint, disabled: this.#isSystemManaged }],
hasDictionary: [{ value: !!this.entity?.hasDictionary, disabled: this.#isSystemManaged }],
caseSensitive: [{ value: this.entity ? !this.entity.caseInsensitive : false, disabled: this.#isSystemManaged }],
@@ -94,17 +118,17 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
Object.assign(controlsConfig, {
defaultReason: [{ value: null, disabled: true }],
});
-
- if (this.entity?.hasDictionary) {
- Object.assign(controlsConfig, {
- addToDictionaryAction: [this.#addToDictionaryActionControl],
- });
- }
}
+
+ if (this.entity?.hasDictionary) {
+ Object.assign(controlsConfig, {
+ addToDictionaryAction: [this.#addToDictionaryActionControl],
+ });
+ }
+
const form = this._formBuilder.group(controlsConfig);
- this.hasHexColor$ = this._colorEmpty$(form, 'hexColor');
- this.hasRecommendationHexColor$ = this._colorEmpty$(form, 'recommendationHexColor');
+ this.#initializeColors(form);
this.technicalName$ = form.get('label').valueChanges.pipe(map((value: string) => this._toTechnicalName(value)));
form.get('hint').valueChanges.subscribe(isHint => {
@@ -112,15 +136,11 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
REDACTION_FIELDS.forEach(field => form.removeControl(field));
} else {
form.addControl('defaultReason', new UntypedFormControl({ value: null, disabled: true }));
-
- if (form.get('hasDictionary').value) {
- form.addControl('addToDictionaryAction', new UntypedFormControl(this.#addToDictionaryActionControl));
- }
}
});
form.get('hasDictionary').valueChanges.subscribe(hasDictionary => {
- if (hasDictionary && !form.get('hint').value) {
+ if (hasDictionary) {
form.addControl('addToDictionaryAction', new UntypedFormControl(this.#addToDictionaryActionControl));
} else {
form.removeControl('addToDictionaryAction');
@@ -171,6 +191,7 @@ export class AddEditEntityComponent extends BaseFormComponent implements OnInit
description: this.form.get('description').value,
hexColor: this.form.get('hexColor').value,
recommendationHexColor: this.form.get('recommendationHexColor').value,
+ skippedHexColor: this.form.get('skippedHexColor').value,
hint: this.form.get('hint').value,
rank: this.form.get('rank').value,
dossierTemplateId: this.dossierTemplateId,
diff --git a/apps/red-ui/src/app/modules/archive/components/table-item/table-item.component.html b/apps/red-ui/src/app/modules/archive/components/table-item/table-item.component.html
index d94dbc95f..ee66ff76b 100644
--- a/apps/red-ui/src/app/modules/archive/components/table-item/table-item.component.html
+++ b/apps/red-ui/src/app/modules/archive/components/table-item/table-item.component.html
@@ -2,7 +2,9 @@
diff --git a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.scss b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.scss
index 4160bf43b..e544be4f2 100644
--- a/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.scss
+++ b/apps/red-ui/src/app/modules/dashboard/components/template-stats/template-stats.component.scss
@@ -17,7 +17,7 @@
&:not(.empty) {
&:hover {
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-side-nav);
.heading {
text-decoration: underline;
diff --git a/apps/red-ui/src/app/modules/dashboard/dashboard-screen/dashboard-screen.component.scss b/apps/red-ui/src/app/modules/dashboard/dashboard-screen/dashboard-screen.component.scss
index f646df137..b782d561c 100644
--- a/apps/red-ui/src/app/modules/dashboard/dashboard-screen/dashboard-screen.component.scss
+++ b/apps/red-ui/src/app/modules/dashboard/dashboard-screen/dashboard-screen.component.scss
@@ -1,6 +1,6 @@
:host {
align-items: center;
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-alt-background);
.container {
padding: 32px;
diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss
index aa6d3e1a6..42741d8ec 100644
--- a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss
+++ b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details-stats/dossier-details-stats.component.scss
@@ -21,7 +21,7 @@
transition: background-color 0.2s;
&:hover {
- background-color: var(--iqser-grey-6);
+ background-color: var(--iqser-btn-bg);
}
}
}
diff --git a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.scss b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.scss
index 73039cfb8..e415ad1bc 100644
--- a/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.scss
+++ b/apps/red-ui/src/app/modules/dossier-overview/components/dossier-details/dossier-details.component.scss
@@ -29,7 +29,7 @@
padding: 3px 8px;
&:hover {
- background-color: var(--iqser-grey-6);
+ background-color: var(--iqser-tab-hover);
}
&.active {
diff --git a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss
index b5a1b4580..522416718 100644
--- a/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss
+++ b/apps/red-ui/src/app/modules/dossiers-listing/components/dossiers-listing-details/dossiers-listing-details.component.scss
@@ -31,5 +31,5 @@
}
.right-chart {
- border-left: 1px solid rgba(226, 228, 233, 0.9);
+ border-left: 1px solid var(--iqser-separator);
}
diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.scss b/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.scss
index a3b6a8184..1110e38ca 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.scss
+++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-details/annotation-details.component.scss
@@ -28,7 +28,7 @@
height: 24px;
&:hover {
- background-color: var(--iqser-grey-6);
+ background-color: var(--iqser-tab-hover);
border-radius: 12px;
mat-icon {
diff --git a/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.scss b/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.scss
index a6653f9b5..6817bb6ea 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.scss
+++ b/apps/red-ui/src/app/modules/file-preview/components/annotation-wrapper/annotation-wrapper.component.scss
@@ -52,7 +52,7 @@
}
&:hover {
- background-color: var(--iqser-grey-4);
+ background-color: var(--iqser-annotation-comments-hover);
}
}
}
@@ -60,7 +60,7 @@
&:hover,
&.help-mode {
- background-color: var(--iqser-grey-8);
+ background-color: var(--iqser-annotation-hover);
::ng-deep .annotation-actions {
display: flex;
diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.scss b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.scss
index c7ddef779..855230586 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.scss
+++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.scss
@@ -78,7 +78,7 @@
outline: none;
&.active-panel {
- background-color: #fafafa;
+ background-color: var(--iqser-workload-pages-bg);
}
}
@@ -98,7 +98,7 @@
transition: background-color 0.25s;
&:not(.disabled):hover {
- background-color: var(--iqser-grey-6);
+ background-color: var(--iqser-tab-hover);
}
mat-icon {
diff --git a/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.scss b/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.scss
index 818da8a29..e248fd1fa 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.scss
+++ b/apps/red-ui/src/app/modules/file-preview/components/page-indicator/page-indicator.component.scss
@@ -1,12 +1,12 @@
.page-wrapper {
- color: var(--iqser-accent);
+ color: var(--iqser-text);
position: relative;
padding: 12px 14px 12px 8px;
cursor: pointer;
border-left: 4px solid transparent;
&:hover {
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-alt-background);
}
&.active {
@@ -33,10 +33,10 @@
}
&.read {
- color: var(--iqser-grey-5);
+ color: var(--iqser-inputs-outline);
.text {
- color: var(--iqser-accent);
+ color: var(--iqser-text);
}
}
diff --git a/apps/red-ui/src/app/modules/file-preview/components/type-annotation-icon/type-annotation-icon.component.ts b/apps/red-ui/src/app/modules/file-preview/components/type-annotation-icon/type-annotation-icon.component.ts
index 0bc6e7af0..e81375668 100644
--- a/apps/red-ui/src/app/modules/file-preview/components/type-annotation-icon/type-annotation-icon.component.ts
+++ b/apps/red-ui/src/app/modules/file-preview/components/type-annotation-icon/type-annotation-icon.component.ts
@@ -32,7 +32,12 @@ export class TypeAnnotationIconComponent implements OnChanges {
this.color = this.annotation.color;
} else {
const type = this.annotation.isSuperTypeBasedColor ? this.annotation.superType : this.annotation.type;
- this.color = this._dictionariesMapService.getDictionaryColor(type, this._dossierTemplateId, this.annotation.isRecommendation);
+ this.color = this._dictionariesMapService.getDictionaryColor(
+ type,
+ this._dossierTemplateId,
+ this.annotation.isRecommendation,
+ this.annotation.isSkipped,
+ );
}
this.type =
diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.scss b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.scss
index f149bcbab..e658d2b05 100644
--- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.scss
+++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.scss
@@ -1,7 +1,7 @@
.vertical-line {
width: 1px;
height: 30px;
- background-color: var(--iqser-grey-4);
+ background-color: var(--iqser-separator);
margin: 0 16px;
}
diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
index cf8b02fd6..61d85c707 100644
--- a/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/file-preview/file-preview-screen.component.ts
@@ -6,7 +6,6 @@ import {
bool,
CircleButtonTypes,
CustomError,
- Debounce,
ErrorService,
FilterService,
List,
@@ -92,7 +91,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private readonly _router: Router,
private readonly _ngZone: NgZone,
private readonly _logger: NGXLogger,
- private readonly _filesService: FilesService,
private readonly _annotationManager: REDAnnotationManager,
private readonly _errorService: ErrorService,
private readonly _filterService: FilterService,
@@ -307,14 +305,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
}
- viewerPageChanged(page: number) {
- // this.multiSelectService.deactivate();
- return this.#updateQueryParamsPage(page);
- }
-
- @Debounce(100)
async viewerReady() {
- // Go to initial page from query params
const pageNumber: string = this._activatedRoute.snapshot.queryParams.page;
if (pageNumber) {
const file = this.state.file;
@@ -327,7 +318,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
page = file.numberOfPages;
}
- this.pdf.navigateTo(page);
+ setTimeout(() => {
+ this.pdf.navigateTo(page);
+ }, 300);
}
this._loadingService.stop();
@@ -347,7 +340,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async downloadOriginalFile({ cacheIdentifier, dossierId, fileId, filename }: File) {
const fileManagementService = this._injector.get(FileManagementService);
- const originalFile = fileManagementService.downloadOriginalFile(dossierId, fileId, 'response', cacheIdentifier);
+ const originalFile = fileManagementService.downloadOriginal(dossierId, fileId, 'response', cacheIdentifier);
download(await firstValueFrom(originalFile), filename);
}
@@ -368,21 +361,24 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
pairwise(),
tap(annotations => this.deleteAnnotations(...annotations)),
);
- const currentPageAnnotations$ = combineLatest([this.pdf.currentPage$, annotations$]).pipe(
+
+ const currentPageIfNotHighlightsView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe(
+ filter(([, viewMode]) => viewMode !== ViewModes.TEXT_HIGHLIGHTS),
+ map(([page]) => page),
+ );
+
+ const currentPageAnnotations$ = combineLatest([currentPageIfNotHighlightsView$, annotations$]).pipe(
map(
([page, [oldAnnotations, newAnnotations]]) =>
[oldAnnotations.filter(byPage(page)), newAnnotations.filter(byPage(page))] as const,
),
);
- let start;
return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe(
filter(([, loaded]) => loaded),
- tap(() => (start = new Date().getTime())),
map(([annotations]) => annotations),
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
- tap(() => this._logger.info(`[ANNOTATIONS] Processing time: ${new Date().getTime() - start}`)),
tap(() => this.updateViewMode()),
);
}
@@ -430,7 +426,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
#rebuildFilters() {
const startTime = new Date().getTime();
- const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this._fileDataService.all);
+ const annotationFilters = this._annotationProcessingService.getAnnotationFilter();
const primaryFilters = this._filterService.getGroup('primaryFilters')?.filters;
this._filterService.addFilterGroup({
slug: 'primaryFilters',
@@ -451,6 +447,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
async #updateQueryParamsPage(page: number): Promise
{
+ console.log('updateQueryParamsPage: ', page);
const extras: NavigationExtras = {
queryParams: { page },
queryParamsHandling: 'merge',
@@ -549,7 +546,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
.pipe(
switchMap(blob => from(this._documentViewer.lock()).pipe(map(() => blob))),
tap(() => this._errorService.clear()),
- tap(blob => this.pdf.loadDocument(blob, this.state.file)),
+ tap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())),
)
.subscribe();
@@ -558,7 +555,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
});
this.addActiveScreenSubscription = this.pdfProxyService.pageChanged$.subscribe(page =>
- this._ngZone.run(() => this.viewerPageChanged(page)),
+ this._ngZone.run(() => {
+ console.log('viewerPageChanged', page);
+ return this.#updateQueryParamsPage(page);
+ }),
);
this.addActiveScreenSubscription = this.pdfProxyService.annotationSelected$.subscribe();
}
@@ -587,30 +587,28 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
if (!newAnnotations.length) {
return;
}
+
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
this.#rebuildFilters();
- const startTime = new Date().getTime();
-
if (currentFilters) {
- this._handleDeltaAnnotationFilters(currentFilters, this._fileDataService.all);
+ this._handleDeltaAnnotationFilters(currentFilters);
}
await this._annotationDrawService.draw(newAnnotations, this.state.dossierTemplateId, this._skippedService.hideSkipped);
- this._logger.info(`[ANNOTATIONS] Redraw time: ${new Date().getTime() - startTime} ms for ${newAnnotations.length} annotations`);
}
- private _handleDeltaAnnotationFilters(currentFilters: NestedFilter[], newAnnotations: AnnotationWrapper[]) {
+ private _handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {
const primaryFilterGroup = this._filterService.getGroup('primaryFilters');
const primaryFilters = primaryFilterGroup.filters;
const secondaryFilters = this._filterService.getGroup('secondaryFilters').filters;
- const hasAnyFilterSet = [...primaryFilters, ...secondaryFilters].find(f => f.checked || f.indeterminate);
+ const hasAnyFilterSet = [...primaryFilters, ...secondaryFilters].some(f => f.checked || f.indeterminate);
if (!hasAnyFilterSet) {
return;
}
- const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(newAnnotations);
+ const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter();
handleFilterDelta(currentFilters, newPageSpecificFilters, primaryFilters);
this._filterService.addFilterGroup({
@@ -626,7 +624,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
}
- private _setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal: boolean = false) {
+ private _setAnnotationsOpacity(annotations: Annotation[], restoreToOriginal = false) {
annotations.forEach(annotation => {
annotation['Opacity'] = restoreToOriginal ? parseFloat(annotation.getCustomData('opacity')) : 1;
});
diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts
index 4bda12b80..4850ad44d 100644
--- a/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts
+++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { SuperTypeSorter } from '../../../utils';
import { Filter, handleCheckedValue, IFilter, INestedFilter, NestedFilter } from '@iqser/common-ui';
@@ -7,10 +7,13 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IViewedPage } from '@red/domain';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { FilePreviewStateService } from './file-preview-state.service';
+import { FileDataService } from './file-data.service';
@Injectable()
export class AnnotationProcessingService {
- constructor(private readonly _state: FilePreviewStateService, private readonly _dictionariesMapService: DictionariesMapService) {}
+ readonly #fileDataService = inject(FileDataService);
+ readonly #state = inject(FilePreviewStateService);
+ readonly #dictionariesMapService = inject(DictionariesMapService);
static secondaryAnnotationFilters(viewedPages?: IViewedPage[]): INestedFilter[] {
const _viewedPages = viewedPages ? viewedPages.map(page => page.page) : [];
@@ -50,11 +53,11 @@ export class AnnotationProcessingService {
].map(item => new NestedFilter(item));
}
- getAnnotationFilter(annotations: AnnotationWrapper[]): INestedFilter[] {
+ getAnnotationFilter(): INestedFilter[] {
const filterMap = new Map();
const filters: INestedFilter[] = [];
- annotations?.forEach(a => {
+ this.#fileDataService.all?.forEach(a => {
const topLevelFilter = a.topLevelFilter;
const filter = filterMap.get(a.filterKey);
if (filter) {
@@ -76,8 +79,8 @@ export class AnnotationProcessingService {
}
const dictionary =
a.type === 'dossier_redaction'
- ? this._state.dossierDictionary
- : this._dictionariesMapService.getDictionary(a.type, this._state.dossierTemplateId);
+ ? this.#state.dossierDictionary
+ : this.#dictionariesMapService.getDictionary(a.type, this.#state.dossierTemplateId);
const childFilter: IFilter = {
id: a.filterKey,
label: dictionary.label,
@@ -212,11 +215,13 @@ export class AnnotationProcessingService {
if (first.pageNumber === second.pageNumber) {
if (first.y > second.y) {
return -1;
- } else if (first.y < second.y) {
- return 1;
- } else {
- return first.x < second.x ? -1 : 1;
}
+
+ if (first.y < second.y) {
+ return 1;
+ }
+
+ return first.x < second.x ? -1 : 1;
}
return first.pageNumber < second.pageNumber ? -1 : 1;
});
diff --git a/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts b/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
index 2f0e883c9..4cc249c90 100644
--- a/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
+++ b/apps/red-ui/src/app/modules/file-preview/services/file-preview-state.service.ts
@@ -4,7 +4,7 @@ import { Dictionary, Dossier, DOSSIER_ID, File, FILE_ID } from '@red/domain';
import { ActivatedRoute, Router } from '@angular/router';
import { FilesMapService } from '@services/files/files-map.service';
import { PermissionsService } from '@services/permissions.service';
-import { boolFactory } from '@iqser/common-ui';
+import { boolFactory, LoadingService } from '@iqser/common-ui';
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { FileManagementService } from '@services/files/file-management.service';
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
@@ -12,6 +12,21 @@ import { wipeFilesCache } from '@red/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';
+
+const ONE_MEGABYTE = 1024 * 1024;
+
+function getRemainingTime(event: HttpProgressEvent, startTime: number) {
+ const currTime = new Date().getTime();
+ const remaining = event.total - event.loaded;
+ const speed = event.loaded / ((currTime - startTime) / 1000);
+ return Math.round(remaining / speed);
+}
+
+function isDownload(event: HttpEvent): event is HttpProgressEvent {
+ return event.type === HttpEventType.DownloadProgress && event.total > ONE_MEGABYTE;
+}
@Injectable()
export class FilePreviewStateService {
@@ -40,6 +55,8 @@ export class FilePreviewStateService {
private readonly _dossiersService: DossiersService,
private readonly _fileManagementService: FileManagementService,
private readonly _dictionaryService: DictionaryService,
+ private readonly _translateService: TranslateService,
+ private readonly _loadingService: LoadingService,
) {
const dossiersService = dossiersServiceResolver(_injector, router);
@@ -89,6 +106,16 @@ export class FilePreviewStateService {
this.#reloadBlob$.next(true);
}
+ #getRemainingTimeVerbose(event: HttpProgressEvent, startTime: number) {
+ const remainingTime = getRemainingTime(event, startTime);
+ if (remainingTime > 60) {
+ const minutes: string = this._translateService.instant('minutes');
+ return `${Math.round(remainingTime / 60)} ${minutes}`;
+ }
+ const seconds: string = this._translateService.instant('seconds');
+ return `${remainingTime} ${seconds}`;
+ }
+
#dossierFilesChange$() {
return this._dossiersService.dossierFileChanges$.pipe(
filter(dossierId => dossierId === this.dossierId),
@@ -97,8 +124,36 @@ export class FilePreviewStateService {
}
#downloadOriginalFile(cacheIdentifier: string, wipeCaches = true): Observable {
- const downloadFile = this._fileManagementService.downloadOriginalFile(this.dossierId, this.fileId, 'body', cacheIdentifier);
+ const downloadFile$ = this.#getFileToDownload(cacheIdentifier);
const obs = wipeCaches ? from(wipeFilesCache()) : of({});
- return obs.pipe(switchMap(() => downloadFile));
+ return obs.pipe(switchMap(() => downloadFile$));
+ }
+
+ #getFileToDownload(cacheIdentifier: string): Observable {
+ const downloadFile$ = this._fileManagementService.downloadOriginal(this.dossierId, this.fileId, 'events', cacheIdentifier);
+ let start;
+ return downloadFile$.pipe(
+ tap(() => (start ? undefined : (start = new Date().getTime()))),
+ tap(event => this.#showLoadingIfIsDownloadEvent(event, start)),
+ filter(event => event.type === HttpEventType.Response),
+ map((event: HttpResponse) => event.body),
+ );
+ }
+
+ #showLoadingIfIsDownloadEvent(event: HttpEvent, start) {
+ if (isDownload(event)) {
+ this.#updateDownloadProgress(event, start);
+ }
+ }
+
+ #updateDownloadProgress(event: HttpProgressEvent, startTime: number) {
+ const progress = Math.round((event.loaded / event.total) * 100);
+ const loading: string = this._translateService.instant('loading');
+ this._loadingService.update({
+ title: loading + ' ' + this.file.filename,
+ type: 'progress-bar',
+ value: progress,
+ remainingTime: this.#getRemainingTimeVerbose(event, startTime),
+ });
}
}
diff --git a/apps/red-ui/src/app/modules/pdf-viewer/components/paginator/paginator.component.scss b/apps/red-ui/src/app/modules/pdf-viewer/components/paginator/paginator.component.scss
index 21be9c450..7a34c7e8e 100644
--- a/apps/red-ui/src/app/modules/pdf-viewer/components/paginator/paginator.component.scss
+++ b/apps/red-ui/src/app/modules/pdf-viewer/components/paginator/paginator.component.scss
@@ -4,7 +4,7 @@
bottom: 20px;
left: calc(50% - (var(--workload-width) / 2));
transform: translate(-50%);
- background: var(--iqser-white);
+ background: var(--iqser-background);
color: var(--iqser-grey-7);
border: 1px solid var(--iqser-grey-7);
border-radius: 8px;
@@ -32,6 +32,7 @@
margin: 0;
}
+ background-color: var(--iqser-background);
color: var(--iqser-grey-7);
text-decoration: none;
outline: none;
@@ -55,7 +56,7 @@
padding-right: 4px;
&:hover {
- color: var(--iqser-accent);
+ color: var(--iqser-text);
}
}
}
diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts
index a27bb65b0..ee998df83 100644
--- a/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts
+++ b/apps/red-ui/src/app/modules/pdf-viewer/services/annotation-draw.service.ts
@@ -75,7 +75,7 @@ export class AnnotationDrawService {
color = this._dictionariesMapService.getDictionaryColor(dictionary, dossierTemplateId, true);
break;
case SuperTypes.Skipped:
- color = this._dictionariesMapService.getDictionaryColor(superType, dossierTemplateId);
+ color = this._dictionariesMapService.getDictionaryColor(dictionary, dossierTemplateId, false, true);
break;
default:
color = this._dictionariesMapService.getDictionaryColor(superType, dossierTemplateId);
@@ -85,9 +85,14 @@ export class AnnotationDrawService {
}
private async _draw(annotationWrappers: List, dossierTemplateId: string, hideSkipped: boolean) {
+ const totalPages = await firstValueFrom(this._pdf.totalPages$);
const annotations = annotationWrappers
- .map(annotation => this._computeAnnotation(annotation, dossierTemplateId, hideSkipped))
- .filter(a => !!a);
+ .map(annotation => this._computeAnnotation(annotation, dossierTemplateId, hideSkipped, totalPages))
+ .filterTruthy();
+ const documentLoaded = await firstValueFrom(this._documentViewer.loaded$);
+ if (!documentLoaded) {
+ return;
+ }
await this._annotationManager.add(annotations);
if (this._userPreferenceService.areDevFeaturesEnabled) {
@@ -130,9 +135,9 @@ export class AnnotationDrawService {
return rectangleAnnot;
}
- private _computeAnnotation(annotationWrapper: AnnotationWrapper, dossierTemplateId: string, hideSkipped: boolean) {
+ private _computeAnnotation(annotationWrapper: AnnotationWrapper, dossierTemplateId: string, hideSkipped: boolean, totalPages: number) {
const pageNumber = this._pdf.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
- if (pageNumber > this._pdf.pageCount) {
+ if (pageNumber > totalPages) {
// skip imported annotations from files that have more pages than the current one
return;
}
diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts
index 75b09d978..22042bfe0 100644
--- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts
+++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts
@@ -1,4 +1,4 @@
-import { Injectable } from '@angular/core';
+import { inject, Injectable } from '@angular/core';
import { Core } from '@pdftron/webviewer';
import { NGXLogger } from 'ngx-logger';
import { fromEvent, merge, Observable } from 'rxjs';
@@ -22,12 +22,10 @@ export class REDDocumentViewer {
selectedText = '';
#document: DocumentViewer;
- constructor(
- private readonly _logger: NGXLogger,
- private readonly _userPreferenceService: UserPreferenceService,
- private readonly _pdf: PdfViewer,
- private readonly _activatedRoute: ActivatedRoute,
- ) {}
+ readonly #logger = inject(NGXLogger);
+ readonly #userPreferenceService = inject(UserPreferenceService);
+ readonly #pdf = inject(PdfViewer);
+ readonly #activatedRoute = inject(ActivatedRoute);
get PDFDoc() {
return this.document?.getPDFDoc();
@@ -41,7 +39,7 @@ export class REDDocumentViewer {
const event$ = fromEvent(this.#document, 'documentUnloaded');
const toBool$ = event$.pipe(map(() => false));
- return toBool$.pipe(tap(() => this._logger.info('[PDF] Document unloaded')));
+ return toBool$.pipe(tap(() => this.#logger.info('[PDF] Document unloaded')));
}
get #documentLoaded$() {
@@ -52,7 +50,7 @@ export class REDDocumentViewer {
tap(() => this.#setCurrentPage()),
tap(() => this.#setInitialDisplayMode()),
tap(() => this.updateTooltipsVisibility()),
- tap(() => this._logger.info('[PDF] Document loaded')),
+ tap(() => this.#logger.info('[PDF] Document loaded')),
);
}
@@ -73,13 +71,7 @@ export class REDDocumentViewer {
get #textSelected$(): Observable {
return fromEvent<[Quad, string, number]>(this.#document, 'textSelected').pipe(
tap(([, selectedText]) => (this.selectedText = selectedText)),
- tap(([, , pageNumber]) => {
- if (this._pdf.isCompare && pageNumber % 2 === 0) {
- this._pdf.disable('textPopup');
- } else {
- this._pdf.enable('textPopup');
- }
- }),
+ tap(([, , pageNumber]) => this.#disableTextPopupIfCompareMode(pageNumber)),
map(([, selectedText]) => selectedText),
);
}
@@ -89,14 +81,14 @@ export class REDDocumentViewer {
}
close() {
- this._logger.info('[PDF] Closing document');
+ this.#logger.info('[PDF] Closing document');
this.#document.closeDocument();
- this._pdf.closeCompareMode();
+ this.#pdf.closeCompareMode();
}
updateTooltipsVisibility(): void {
- const current = this._userPreferenceService.getFilePreviewTooltipsPreference();
- this._pdf.instance.UI.setAnnotationContentOverlayHandler(() => (current ? undefined : false));
+ const current = this.#userPreferenceService.getFilePreviewTooltipsPreference();
+ this.#pdf.instance.UI.setAnnotationContentOverlayHandler(() => (current ? undefined : false));
}
init(document: DocumentViewer) {
@@ -114,7 +106,7 @@ export class REDDocumentViewer {
}
await document.lock();
- this._logger.info('[PDF] Locked');
+ this.#logger.info('[PDF] Locked');
return true;
}
@@ -156,7 +148,7 @@ export class REDDocumentViewer {
pages.forEach(page => this.#document.setRotation(0, Number(page)));
}
- rotate(rotation: RotationType, page = this._pdf.currentPage) {
+ rotate(rotation: RotationType, page = this.#pdf.currentPage) {
if (rotation === RotationTypes.LEFT) {
this.#document.rotateCounterClockwise(page);
} else {
@@ -164,16 +156,24 @@ export class REDDocumentViewer {
}
}
+ #disableTextPopupIfCompareMode(pageNumber) {
+ if (this.#pdf.isCompare && pageNumber % 2 === 0) {
+ return this.#pdf.disable('textPopup');
+ }
+
+ this.#pdf.enable('textPopup');
+ }
+
#setCurrentPage() {
- const currentDocPage = this._activatedRoute.snapshot.queryParamMap.get('page');
- this.#document.setCurrentPage(Number(currentDocPage ?? '1'), false);
+ const currentDocPage = this.#activatedRoute.snapshot.queryParamMap.get('page');
+ this.#pdf.navigateTo(currentDocPage ?? 1);
}
#setInitialDisplayMode() {
- this._pdf.instance.UI.setFitMode('FitPage');
+ this.#pdf.instance.UI.setFitMode('FitPage');
const displayModeManager = this.#document.getDisplayModeManager();
const instanceDisplayMode = displayModeManager.getDisplayMode();
- instanceDisplayMode.mode = this._pdf.isCompare ? 'Facing' : 'Single';
+ instanceDisplayMode.mode = this.#pdf.isCompare ? 'Facing' : 'Single';
displayModeManager.setDisplayMode(instanceDisplayMode);
}
}
diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts
index 8f4e2d850..6adad4f0a 100644
--- a/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts
+++ b/apps/red-ui/src/app/modules/pdf-viewer/services/pdf-viewer.service.ts
@@ -13,6 +13,7 @@ import { Rgb } from '../utils/types';
import { asList } from '../utils/functions';
import { TranslateService } from '@ngx-translate/core';
import { LicenseService } from '@services/license.service';
+import { UserPreferenceService } from '@services/user-preference.service';
import TextTool = Core.Tools.TextTool;
import Annotation = Core.Annotations.Annotation;
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
@@ -53,6 +54,7 @@ export class PdfViewer {
private readonly _activatedRoute: ActivatedRoute,
private readonly _licenseService: LicenseService,
private readonly _translateService: TranslateService,
+ private readonly _userPreferenceService: UserPreferenceService,
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
) {}
@@ -133,6 +135,7 @@ export class PdfViewer {
this.#instance = await this.#getInstance(htmlElement);
await this.PDFNet.initialize(this._licenseService.activeLicenseKey);
+ this.#instance.UI.setTheme(this._userPreferenceService.getTheme());
this._logger.info('[PDF] Initialized');
this.documentViewer = this.#instance.Core.documentViewer;
@@ -166,11 +169,11 @@ export class PdfViewer {
this.#compareMode$.next(false);
}
- async loadDocument(blob: Blob, file: File) {
+ async loadDocument(blob: Blob, file: File, actionOnError: () => void = () => {}) {
const onError = () => {
this._injector.get(ErrorService).set(DOCUMENT_LOADING_ERROR);
this._logger.error('[PDF] Error while loading document');
- // this.stateService.reloadBlob();
+ actionOnError();
};
const document = await this.PDFNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.scss b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.scss
index 593f3c7ae..091142726 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.scss
+++ b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.scss
@@ -1,8 +1,8 @@
-@use 'libs/common-ui/src/assets/styles/common-mixins';
+@use 'common-mixins';
.file-actions {
display: flex;
- color: var(--iqser-grey-1);
+ color: var(--iqser-text);
> *:not(:last-child) {
margin-right: 2px;
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts
index de8bee50e..de12143e1 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts
+++ b/apps/red-ui/src/app/modules/shared-dossiers/components/file-actions/file-actions.component.ts
@@ -10,7 +10,7 @@ import {
ViewChild,
} from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
-import { Action, ActionTypes, Dossier, File } from '@red/domain';
+import { Action, ActionTypes, Dossier, File, User } from '@red/domain';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import {
CircleButtonType,
@@ -46,7 +46,7 @@ import { ROTATION_ACTION_BUTTONS } from '../../../pdf-viewer/utils/constants';
})
export class FileActionsComponent implements OnChanges {
readonly circleButtonTypes = CircleButtonTypes;
- readonly currentUser;
+ readonly currentUser: User;
@Input() file: File;
@Input() dossier: Dossier;
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html
index c446cf066..a2faa635d 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html
+++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.html
@@ -24,19 +24,18 @@
>
-
-
-
-
+
+
+
{{ 'assign-dossier-owner.dialog.no-reviewers' | translate }}
diff --git a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.scss b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.scss
index 0d427a120..51ce0bbb6 100644
--- a/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.scss
+++ b/apps/red-ui/src/app/modules/shared-dossiers/dialogs/edit-dossier-dialog/edit-dossier-team/edit-dossier-team.component.scss
@@ -54,7 +54,7 @@ redaction-team-members {
&.selected,
&:hover {
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-alt-background);
.actions {
display: flex;
diff --git a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.scss b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.scss
index 773493760..1bfd8851a 100644
--- a/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.scss
+++ b/apps/red-ui/src/app/modules/shared/components/donut-chart/donut-chart.component.scss
@@ -62,7 +62,7 @@
}
&:hover:not(.active):not(.filter-disabled) {
- background-color: var(--iqser-grey-6);
+ background-color: var(--iqser-btn-bg);
}
&.active {
diff --git a/apps/red-ui/src/app/modules/shared/components/dossier-state/dossier-state.component.scss b/apps/red-ui/src/app/modules/shared/components/dossier-state/dossier-state.component.scss
index ce8a15efc..9290cb1ca 100644
--- a/apps/red-ui/src/app/modules/shared/components/dossier-state/dossier-state.component.scss
+++ b/apps/red-ui/src/app/modules/shared/components/dossier-state/dossier-state.component.scss
@@ -10,5 +10,5 @@ redaction-small-chip {
.dossier-state-text {
font-size: 13px;
line-height: 16px;
- color: var(--iqser-grey-1);
+ color: var(--iqser-text);
}
diff --git a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.scss b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.scss
index 862b53102..9afd86ac7 100644
--- a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.scss
+++ b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.scss
@@ -6,7 +6,7 @@ ngx-monaco-editor {
}
%arrow {
- border: solid var(--iqser-grey-1);
+ border: solid var(--iqser-text);
border-width: 2px 0 0 2px;
height: 4px !important;
width: 4px !important;
diff --git a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
index f2cdbe2f2..f70b83f7b 100644
--- a/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
+++ b/apps/red-ui/src/app/modules/shared/components/editor/editor.component.ts
@@ -1,5 +1,7 @@
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { Debounce, List, OnChange } from '@iqser/common-ui';
+import { UserPreferenceService } from '@services/user-preference.service';
+import { EditorThemeService } from '@services/editor-theme.service';
import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorConstructionOptions;
import ICodeEditor = monaco.editor.ICodeEditor;
import IDiffEditor = monaco.editor.IDiffEditor;
@@ -41,6 +43,8 @@ export class EditorComponent implements OnInit, OnChanges {
private _diffEditor: IDiffEditor;
private _decorations: string[] = [];
+ constructor(private readonly _userPreferenceService: UserPreferenceService, private readonly _editorThemeService: EditorThemeService) {}
+
get currentEntries(): string[] {
return this.value.split('\n');
}
@@ -102,31 +106,14 @@ export class EditorComponent implements OnInit, OnChanges {
}
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',
- },
- });
+ for (const theme of this._editorThemeService.themes) {
+ (window as any).monaco.editor.defineTheme(theme, this._editorThemeService.configurations[theme]);
+ }
}
private _setTheme(): void {
this._defineThemes();
- (window as any).monaco.editor.setTheme(this.canEdit ? 'redaction' : 'redaction-disabled');
+ (window as any).monaco.editor.setTheme(this._editorThemeService.getTheme(this.canEdit));
}
private _handleMarginButtonClick(event: IEditorMouseEvent) {
diff --git a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.scss b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.scss
index 822b7dc96..aec40dc04 100644
--- a/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.scss
+++ b/apps/red-ui/src/app/modules/shared/components/expandable-file-actions/expandable-file-actions.component.scss
@@ -18,6 +18,6 @@ mat-slide-toggle {
}
&[disabled] {
- color: rgba(var(--iqser-accent-rgb), 0.3);
+ color: rgba(var(--iqser-text-rgb), 0.3);
}
}
diff --git a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss
index afcada18e..97bfc982c 100644
--- a/apps/red-ui/src/app/modules/shared/components/select/select.component.scss
+++ b/apps/red-ui/src/app/modules/shared/components/select/select.component.scss
@@ -12,7 +12,6 @@
}
}
-// https://stackoverflow.com/questions/34641281/how-to-add-class-to-host-element
:host(.fixed-height) {
height: var(--height);
overflow: hidden;
@@ -65,23 +64,23 @@ mat-chip {
}
.mat-chip.mat-standard-chip.mat-chip-selected.mat-primary {
- background-color: var(--iqser-grey-6);
- color: var(--iqser-accent);
+ background-color: var(--iqser-btn-bg);
+ color: var(--iqser-text);
}
.mat-chip.mat-standard-chip {
- background-color: var(--iqser-white);
- color: var(--iqser-accent);
+ background-color: var(--iqser-background);
+ color: var(--iqser-text);
margin: 0 0 2px 0;
transition: background-color 0.2s;
&:hover {
- background-color: var(--iqser-grey-8);
+ background-color: var(--iqser-not-disabled-table-item);
}
}
.mat-chip.mat-standard-chip::after {
- background: var(--iqser-grey-8);
+ background: transparent;
}
.mat-standard-chip:focus::after {
diff --git a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
index 242c54047..f3ca4fbac 100644
--- a/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
+++ b/apps/red-ui/src/app/modules/upload-download/services/file-upload.service.ts
@@ -215,8 +215,8 @@ export class FileUploadService extends GenericService impleme
private _createSubscription(uploadFile: FileUploadModel) {
this.activeUploads++;
const obs = this.uploadFileForm(uploadFile.dossierId, uploadFile.keepManualRedactions, uploadFile.file);
- return obs.subscribe(
- event => {
+ return obs.subscribe({
+ next: event => {
if (event.type === HttpEventType.UploadProgress) {
uploadFile.progress = Math.round((event.loaded / (event.total || event.loaded)) * 100);
this._applicationRef.tick();
@@ -234,7 +234,7 @@ export class FileUploadService extends GenericService impleme
this._removeUpload(uploadFile);
}
},
- (err: HttpErrorResponse) => {
+ error: (err: HttpErrorResponse) => {
uploadFile.completed = true;
uploadFile.error = {
// Extract error message
@@ -246,7 +246,7 @@ export class FileUploadService extends GenericService impleme
this.scheduleUpload(uploadFile);
}
},
- );
+ });
}
private _removeUpload(fileUploadModel: FileUploadModel) {
diff --git a/apps/red-ui/src/app/modules/upload-download/upload-status-overlay/upload-status-overlay.component.scss b/apps/red-ui/src/app/modules/upload-download/upload-status-overlay/upload-status-overlay.component.scss
index c2569c204..02647eb80 100644
--- a/apps/red-ui/src/app/modules/upload-download/upload-status-overlay/upload-status-overlay.component.scss
+++ b/apps/red-ui/src/app/modules/upload-download/upload-status-overlay/upload-status-overlay.component.scss
@@ -1,15 +1,14 @@
@use 'common-mixins';
-@use 'variables';
.red-upload-download-overlay {
- background: var(--iqser-white);
+ background: var(--iqser-background);
position: fixed;
bottom: 20px;
right: 20px;
- box-shadow: 0 3px 12px 5px rgba(40, 50, 65, 0.14);
border-radius: 8px;
overflow: hidden;
width: 400px;
+ @include common-mixins.drop-shadow;
.red-upload-download-header {
display: flex;
@@ -19,14 +18,14 @@
padding: 16px 14px 16px 16px;
cursor: pointer;
- color: var(--iqser-accent);
+ color: var(--iqser-text);
font-size: 13px;
font-weight: 600;
line-height: 16px;
mat-icon {
height: 13px;
- color: var(--iqser-accent);
+ color: var(--iqser-text);
&.collapse-icon {
height: 15px;
@@ -52,7 +51,7 @@
.dossier-name-wrapper {
display: flex;
padding: 4px 16px;
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-alt-background);
> span {
@include common-mixins.line-clamp(1);
@@ -90,7 +89,7 @@
}
&.error {
- background-color: rgba(variables.$primary, 0.1);
+ background-color: rgba(var(--iqser-primary-rgb), 0.1);
padding-right: 100px;
.error-message {
@@ -113,13 +112,13 @@
}
&:not(.error) {
- background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, variables.$grey-2 35%);
+ background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, var(--iqser-alt-background) 35%);
padding-left: 60px;
}
}
&:not(.error):hover {
- background-color: var(--iqser-grey-2);
+ background-color: var(--iqser-alt-background);
.actions {
display: flex;
diff --git a/apps/red-ui/src/app/services/editor-theme.service.ts b/apps/red-ui/src/app/services/editor-theme.service.ts
new file mode 100644
index 000000000..8033eeef0
--- /dev/null
+++ b/apps/red-ui/src/app/services/editor-theme.service.ts
@@ -0,0 +1,62 @@
+import { Injectable } from '@angular/core';
+import { UserPreferenceService } from './user-preference.service';
+import { editor } from 'monaco-editor';
+import IStandaloneThemeData = editor.IStandaloneThemeData;
+
+@Injectable({
+ providedIn: 'root',
+})
+export class EditorThemeService {
+ readonly themes = ['redaction', 'redaction-disabled', 'redaction-dark', 'redaction-disabled-dark'];
+ readonly configurations: Record = {
+ redaction: {
+ base: 'vs',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editor.lineHighlightBackground': '#f4f5f7',
+ },
+ },
+ 'redaction-disabled': {
+ base: 'vs',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editor.background': '#f4f5f7',
+ 'editor.foreground': '#9398a0',
+ 'editor.lineHighlightBackground': '#f4f5f7',
+ 'editorLineNumber.foreground': '#9398a0',
+ 'editorActiveLineNumber.foreground': '#9398a0',
+ },
+ },
+ 'redaction-dark': {
+ base: 'vs-dark',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editor.background': '#151a21',
+ 'editor.lineHighlightBackground': '#283241',
+ },
+ },
+ 'redaction-disabled-dark': {
+ base: 'vs-dark',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editor.background': '#151a21',
+ 'editor.foreground': '#9398a0',
+ 'editor.lineHighlightBackground': '#283241',
+ 'editorLineNumber.foreground': '#9398a0',
+ 'editorActiveLineNumber.foreground': '#9398a0',
+ },
+ },
+ };
+
+ constructor(private readonly _userPreferenceService: UserPreferenceService) {}
+
+ getTheme(canEdit: boolean): string {
+ const isDarkTheme = this._userPreferenceService.getTheme() === 'dark';
+ const editorTheme = canEdit ? 'redaction' : 'redaction-disabled';
+ return `${editorTheme}${isDarkTheme ? '-dark' : ''}`;
+ }
+}
diff --git a/apps/red-ui/src/app/services/entity-services/dictionaries-map.service.ts b/apps/red-ui/src/app/services/entity-services/dictionaries-map.service.ts
index 248ee722d..a7f3aa9dc 100644
--- a/apps/red-ui/src/app/services/entity-services/dictionaries-map.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/dictionaries-map.service.ts
@@ -13,14 +13,14 @@ export class DictionariesMapService extends EntitiesMapService {
- constructor(
- protected readonly _injector: Injector,
- private readonly _filesService: FilesService,
- private readonly _dossierStatsService: DossierStatsService,
- ) {
+ constructor(protected readonly _injector: Injector, private readonly _filesService: FilesService) {
super(_injector, '');
}
@@ -30,13 +25,13 @@ export class FileManagementService extends GenericService {
return this._post(body, `rotate/${dossierId}/${fileId}`);
}
- downloadOriginalFile(dossierId: string, fileId: string, observe?: 'body', indicator?: string): Observable;
- downloadOriginalFile(dossierId: string, fileId: string, observe?: 'response', indicator?: string): Observable>;
+ downloadOriginal(dossierId: string, fileId: string, observe?: 'events', indicator?: string): Observable>;
+ downloadOriginal(dossierId: string, fileId: string, observe?: 'response', indicator?: string): Observable>;
@Validate()
- downloadOriginalFile(
+ downloadOriginal(
@RequiredParam() dossierId: string,
@RequiredParam() fileId: string,
- observe: 'body' | 'response' = 'body',
+ observe: 'events' | 'response' = 'events',
indicator?: string,
) {
const queryParams: QueryParam[] = [{ key: 'inline', value: true }];
@@ -56,6 +51,7 @@ export class FileManagementService extends GenericService {
params: this._queryParams(queryParams),
headers: headers,
observe: observe,
+ reportProgress: observe === 'events',
});
}
}
diff --git a/apps/red-ui/src/app/services/user-preference.service.ts b/apps/red-ui/src/app/services/user-preference.service.ts
index 5df934cac..d426b3349 100644
--- a/apps/red-ui/src/app/services/user-preference.service.ts
+++ b/apps/red-ui/src/app/services/user-preference.service.ts
@@ -9,6 +9,7 @@ const KEYS = {
dossierRecent: 'Dossier-Recent',
filePreviewTooltips: 'File-Preview-Tooltips',
lastDossierTemplate: 'Last-Dossier-Template',
+ theme: 'Theme',
} as const;
@Injectable({
@@ -46,6 +47,15 @@ export class UserPreferenceService extends GenericService {
await this._save(KEYS.lastDossierTemplate, dossierTemplateId);
}
+ getTheme(): string {
+ return this._getAttribute(KEYS.theme, 'light');
+ }
+
+ async saveTheme(theme: 'light' | 'dark'): Promise {
+ await this._save(KEYS.theme, theme);
+ window.location.reload();
+ }
+
getLanguage(): string {
return this._getAttribute(KEYS.language);
}
@@ -64,7 +74,7 @@ export class UserPreferenceService extends GenericService {
}
toggleDevFeatures(): void {
- sessionStorage.setItem('redaction.enable-dev-features', `${!this.areDevFeaturesEnabled}`);
+ sessionStorage.setItem('redaction.enable-dev-features', String(!this.areDevFeaturesEnabled));
window.location.reload();
}
diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json
index aac659a62..d849c4ea0 100644
--- a/apps/red-ui/src/assets/i18n/de.json
+++ b/apps/red-ui/src/assets/i18n/de.json
@@ -100,6 +100,8 @@
"recommendation-color": "",
"recommendation-color-placeholder": "",
"redaction": "",
+ "skipped-color": "",
+ "skipped-color-placeholder": "",
"technical-name": "",
"technical-name-hint": ""
},
@@ -1595,6 +1597,7 @@
"usage-details": "Nutzungsdetails"
},
"license-information": "Lizenzinformationen",
+ "loading": "",
"manual-annotation": {
"dialog": {
"actions": {
@@ -1628,6 +1631,7 @@
}
}
},
+ "minutes": "",
"notification": {
"assign-approver": "Sie wurden dem Dokument {fileHref, select, null{{fileName}} other{{fileName}}} im Dossier {dossierHref, select, null{{dossierName}} other{{dossierName}}} als Genehmiger zugewiesen!",
"assign-reviewer": "Sie wurden dem Dokument {fileHref, select, null{{fileName}} other{{fileName}}} im Dossier {dossierHref, select, null{{dossierName}} other{{dossierName}}} als Reviewer zugewiesen!",
@@ -1922,6 +1926,7 @@
"placeholder": "Nach Dokumenten oder Dokumenteninhalt suchen",
"this-dossier": "in diesem Dossier"
},
+ "seconds": "",
"size": "",
"smtp-auth-config": {
"actions": {
@@ -2050,6 +2055,7 @@
"save": "Änderungen speichern"
},
"form": {
+ "dark-theme": "",
"email": "Email",
"first-name": "Vorname",
"last-name": "Nachname"
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index dc8a98b29..58dff7e5d 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -100,6 +100,8 @@
"recommendation-color": "Recommendation Hex Color",
"recommendation-color-placeholder": "#",
"redaction": "Redaction",
+ "skipped-color": "Skipped Hex Color",
+ "skipped-color-placeholder": "#",
"technical-name": "Technical Name",
"technical-name-hint": "{type, select, edit{Autogenerated based on the initial display name.} create{Autogenerates based on the display name and cannot be edited after saving.} other{}}"
},
@@ -1595,6 +1597,7 @@
"usage-details": "Usage Details"
},
"license-information": "License Information",
+ "loading": "Loading",
"manual-annotation": {
"dialog": {
"actions": {
@@ -1628,6 +1631,7 @@
}
}
},
+ "minutes": "minutes",
"notification": {
"assign-approver": "You have been assigned as approver for {fileHref, select, null{{fileName}} other{{fileName}}} in dossier: {dossierHref, select, null{{dossierName}} other{{dossierName}}}!",
"assign-reviewer": "You have been assigned as reviewer for {fileHref, select, null{{fileName}} other{{fileName}}} in dossier: {dossierHref, select, null{{dossierName}} other{{dossierName}}}!",
@@ -1922,6 +1926,7 @@
"placeholder": "Search documents...",
"this-dossier": "in this dossier"
},
+ "seconds": "seconds",
"size": "Size",
"smtp-auth-config": {
"actions": {
@@ -2050,6 +2055,7 @@
"save": "Save Changes"
},
"form": {
+ "dark-theme": "Dark Theme",
"email": "Email",
"first-name": "First name",
"last-name": "Last name"
diff --git a/apps/red-ui/src/assets/icons/general/reference.svg b/apps/red-ui/src/assets/icons/general/reference.svg
index 77e985170..1b88e7b29 100644
--- a/apps/red-ui/src/assets/icons/general/reference.svg
+++ b/apps/red-ui/src/assets/icons/general/reference.svg
@@ -1,10 +1,12 @@
-